builtins.test.ts 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. import { describe, expect } from "bun:test"
  2. import { Effect, Layer } from "effect"
  3. import * as TestClock from "effect/testing/TestClock"
  4. import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
  5. import { LayerNode } from "@opencode-ai/core/effect/layer-node"
  6. import { Location } from "@opencode-ai/core/location"
  7. import { FSUtil } from "@opencode-ai/core/fs-util"
  8. import { Global } from "@opencode-ai/core/global"
  9. import { AbsolutePath } from "@opencode-ai/core/schema"
  10. import { SystemContext } from "@opencode-ai/core/system-context"
  11. import { SystemContextBuiltIns } from "@opencode-ai/core/system-context/builtins"
  12. import { SystemContextRegistry } from "@opencode-ai/core/system-context/registry"
  13. import { location } from "../fixture/location"
  14. import { testEffect } from "../lib/effect"
  15. const directory = AbsolutePath.make(FSUtil.resolve("/repo/packages/core"))
  16. const projectDirectory = AbsolutePath.make(FSUtil.resolve("/repo"))
  17. const instructionFile = FSUtil.resolve("/repo/AGENTS.md")
  18. const timestamp = Date.parse("2026-06-03T12:00:00.000Z")
  19. const localDate = (time: number) => new Date(time).toDateString()
  20. const locationLayer = Layer.succeed(
  21. Location.Service,
  22. Location.Service.of(
  23. location(
  24. { directory },
  25. { projectDirectory, vcs: { type: "git", store: AbsolutePath.make(FSUtil.resolve("/repo/.git")) } },
  26. ),
  27. ),
  28. )
  29. const builtInsNode = LayerNode.group([SystemContextBuiltIns.node, SystemContextRegistry.node])
  30. const it = testEffect(
  31. AppNodeBuilder.build(builtInsNode, [
  32. [Location.node, locationLayer],
  33. [Global.node, Global.layerWith({ config: "/global" })],
  34. ]),
  35. )
  36. const instructionFS = Layer.effect(
  37. FSUtil.Service,
  38. FSUtil.Service.pipe(
  39. Effect.map((fs) =>
  40. FSUtil.Service.of({
  41. ...fs,
  42. up: () => Effect.succeed([instructionFile]),
  43. readFileStringSafe: (path) => Effect.succeed(path === instructionFile ? "Be precise." : undefined),
  44. }),
  45. ),
  46. ),
  47. ).pipe(Layer.provide(LayerNode.compile(FSUtil.node)))
  48. const itWithInstructions = testEffect(
  49. AppNodeBuilder.build(builtInsNode, [
  50. [Location.node, locationLayer],
  51. [FSUtil.node, instructionFS],
  52. [Global.node, Global.layerWith({ config: "/global" })],
  53. ]),
  54. )
  55. describe("SystemContextBuiltIns", () => {
  56. it.effect("loads location-scoped environment and host-local date context", () =>
  57. Effect.gen(function* () {
  58. yield* TestClock.setTime(timestamp)
  59. const context = yield* SystemContextRegistry.Service
  60. const initialized = yield* SystemContext.initialize(yield* context.load())
  61. expect(initialized.baseline).toBe(
  62. [
  63. "Here is some useful information about the environment you are running in:",
  64. "<env>",
  65. ` Working directory: ${directory}`,
  66. ` Workspace root folder: ${projectDirectory}`,
  67. " Is directory a git repo: yes",
  68. ` Platform: ${process.platform}`,
  69. "</env>",
  70. "",
  71. `Today's date: ${localDate(timestamp)}`,
  72. ].join("\n"),
  73. )
  74. }),
  75. )
  76. it.effect("reconciles the date without repeating unchanged environment context", () =>
  77. Effect.gen(function* () {
  78. yield* TestClock.setTime(timestamp)
  79. const context = yield* SystemContextRegistry.Service
  80. const initialized = yield* SystemContext.initialize(yield* context.load())
  81. yield* TestClock.setTime(timestamp + 24 * 60 * 60 * 1000)
  82. const refreshed = yield* SystemContext.reconcile(yield* context.load(), initialized.snapshot)
  83. expect(refreshed).toMatchObject({
  84. _tag: "Updated",
  85. text: `Today's date is now: ${localDate(timestamp + 24 * 60 * 60 * 1000)}`,
  86. })
  87. }),
  88. )
  89. it.effect("does not update again within the same local calendar day", () =>
  90. Effect.gen(function* () {
  91. yield* TestClock.setTime(timestamp)
  92. const context = yield* SystemContextRegistry.Service
  93. const initialized = yield* SystemContext.initialize(yield* context.load())
  94. yield* TestClock.setTime(timestamp + 60 * 60 * 1000)
  95. expect(yield* SystemContext.reconcile(yield* context.load(), initialized.snapshot)).toEqual({ _tag: "Unchanged" })
  96. }),
  97. )
  98. itWithInstructions.effect("composes ambient instructions after built-in context", () =>
  99. Effect.gen(function* () {
  100. yield* TestClock.setTime(timestamp)
  101. const context = yield* SystemContextRegistry.Service
  102. expect((yield* SystemContext.initialize(yield* context.load())).baseline).toBe(
  103. [
  104. "Here is some useful information about the environment you are running in:",
  105. "<env>",
  106. ` Working directory: ${directory}`,
  107. ` Workspace root folder: ${projectDirectory}`,
  108. " Is directory a git repo: yes",
  109. ` Platform: ${process.platform}`,
  110. "</env>",
  111. "",
  112. `Today's date: ${localDate(timestamp)}`,
  113. "",
  114. `Instructions from: ${instructionFile}\nBe precise.`,
  115. ].join("\n"),
  116. )
  117. }),
  118. )
  119. })