@opengeni/runtime 4.3.0 → 4.4.0-canary.36927100590001

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. package/dist/agent-instructions/base-behavior.d.ts +8 -0
  2. package/dist/agent-instructions/compose.d.ts +59 -0
  3. package/dist/agent-instructions/identity.d.ts +21 -0
  4. package/dist/agent-instructions/index.d.ts +6 -0
  5. package/dist/agent-instructions/modules/admin.d.ts +3 -0
  6. package/dist/agent-instructions/modules/artifacts.d.ts +7 -0
  7. package/dist/agent-instructions/modules/attachments.d.ts +3 -0
  8. package/dist/agent-instructions/modules/environment.d.ts +11 -0
  9. package/dist/agent-instructions/modules/goals.d.ts +3 -0
  10. package/dist/agent-instructions/modules/knowledge.d.ts +6 -0
  11. package/dist/agent-instructions/modules/machines.d.ts +3 -0
  12. package/dist/agent-instructions/modules/renderer-markdown.d.ts +6 -0
  13. package/dist/agent-instructions/modules/repositories.d.ts +7 -0
  14. package/dist/agent-instructions/modules/sandbox.d.ts +3 -0
  15. package/dist/agent-instructions/modules/skills.d.ts +3 -0
  16. package/dist/agent-instructions/modules/subagents.d.ts +3 -0
  17. package/dist/agent-instructions/runtime-mechanics.d.ts +7 -0
  18. package/dist/agent-instructions/types.d.ts +55 -0
  19. package/dist/assets/codemode-client.json +1 -1
  20. package/dist/{chunk-JUFVYQCY.js → chunk-VDXKKMMD.js} +457 -165
  21. package/dist/chunk-VDXKKMMD.js.map +1 -0
  22. package/dist/{chunk-22M75RJF.js → chunk-ZY655LYR.js} +1636 -709
  23. package/dist/chunk-ZY655LYR.js.map +1 -0
  24. package/dist/claude-subscription-usage.d.ts +3 -1
  25. package/dist/first-party-client.d.ts +1 -0
  26. package/dist/index.d.ts +37 -28
  27. package/dist/index.js +28 -2
  28. package/dist/input-wait-yield.d.ts +4 -0
  29. package/dist/interaction-tools.d.ts +1 -0
  30. package/dist/lazy-tool-transport.d.ts +6 -2
  31. package/dist/mcp-run-credentials.d.ts +3 -2
  32. package/dist/mcp-transport-logger.d.ts +10 -0
  33. package/dist/model-context-inspector.d.ts +17 -1
  34. package/dist/model-preparation-diagnostics.d.ts +9 -0
  35. package/dist/model-provider-routing.d.ts +4 -0
  36. package/dist/model-provider.d.ts +1 -0
  37. package/dist/model-request-capture.d.ts +9 -0
  38. package/dist/replayable-json-body.d.ts +2 -1
  39. package/dist/responses-terminal-error.d.ts +17 -0
  40. package/dist/sandbox/codemode-authority.d.ts +1 -0
  41. package/dist/sandbox/index.d.ts +3 -3
  42. package/dist/sandbox/index.js +5 -1
  43. package/dist/sandbox/provider-command-session.d.ts +6 -0
  44. package/dist/sandbox/providers/modal-command-control.d.ts +7 -0
  45. package/dist/sandbox/providers/modal-command-router-wire.d.ts +8 -0
  46. package/dist/sandbox/providers/modal-command-session.d.ts +2 -1
  47. package/dist/sandbox/providers/modal-command-start-errors.d.ts +9 -0
  48. package/dist/sandbox/providers/modal.d.ts +3 -0
  49. package/dist/sandbox/routing/routing-session.d.ts +5 -4
  50. package/dist/workspace-tool-gateway.js +2 -2
  51. package/package.json +12 -12
  52. package/src/agent-instructions/PROMPT_CHANGELOG.md +149 -0
  53. package/src/agent-instructions/base-behavior.ts +120 -0
  54. package/src/agent-instructions/compose.ts +153 -0
  55. package/src/agent-instructions/identity.ts +42 -0
  56. package/src/agent-instructions/index.ts +24 -0
  57. package/src/agent-instructions/modules/admin.ts +25 -0
  58. package/src/agent-instructions/modules/artifacts.ts +39 -0
  59. package/src/agent-instructions/modules/attachments.ts +12 -0
  60. package/src/agent-instructions/modules/environment.ts +60 -0
  61. package/src/agent-instructions/modules/goals.ts +15 -0
  62. package/src/agent-instructions/modules/knowledge.ts +21 -0
  63. package/src/agent-instructions/modules/machines.ts +17 -0
  64. package/src/agent-instructions/modules/renderer-markdown.ts +17 -0
  65. package/src/agent-instructions/modules/repositories.ts +28 -0
  66. package/src/agent-instructions/modules/sandbox.ts +63 -0
  67. package/src/agent-instructions/modules/skills.ts +19 -0
  68. package/src/agent-instructions/modules/subagents.ts +42 -0
  69. package/src/agent-instructions/runtime-mechanics.ts +55 -0
  70. package/src/agent-instructions/types.ts +83 -0
  71. package/src/bundled_default_skills/opengeni-client/SKILL.md +116 -37
  72. package/src/bundled_default_skills/opengeni-client/references/agent-recipes.md +18 -8
  73. package/src/bundled_default_skills/opengeni-client/references/api-workflows.md +24 -4
  74. package/src/bundled_default_skills/opengeni-client/references/chat-facade-fallback.md +23 -4
  75. package/src/bundled_default_skills/opengeni-client/references/compatibility-and-troubleshooting.md +34 -1
  76. package/src/bundled_default_skills/opengeni-client/references/configure-the-agent.md +248 -0
  77. package/src/bundled_default_skills/opengeni-client/references/customer-skill-template.md +2 -1
  78. package/src/bundled_default_skills/opengeni-client/references/data-tools-and-credentials.md +15 -4
  79. package/src/bundled_default_skills/opengeni-client/references/discovery-and-autonomy.md +2 -2
  80. package/src/bundled_default_skills/opengeni-client/references/isolation-and-authorization.md +5 -3
  81. package/src/bundled_default_skills/opengeni-client/references/product-integration-shapes.md +6 -5
  82. package/src/bundled_default_skills/opengeni-client/references/product-shapes-and-ui.md +36 -3
  83. package/src/bundled_default_skills/opengeni-client/references/proxy-from-any-backend.md +170 -0
  84. package/src/bundled_default_skills/opengeni-client/references/session-history-import.md +147 -0
  85. package/src/bundled_default_skills/opengeni-client/references/usage-allowances.md +189 -0
  86. package/src/bundled_default_skills/opengeni-help/SKILL.md +1 -1
  87. package/src/claude-subscription-usage.ts +23 -4
  88. package/src/first-party-client.ts +4 -0
  89. package/src/index.ts +260 -146
  90. package/src/input-wait-yield.ts +7 -0
  91. package/src/interaction-tools.ts +16 -2
  92. package/src/lazy-tool-transport.ts +46 -16
  93. package/src/mcp-run-credentials.ts +18 -3
  94. package/src/mcp-transport-logger.ts +46 -0
  95. package/src/model-context-inspector.ts +37 -3
  96. package/src/model-preparation-diagnostics.ts +48 -1
  97. package/src/model-provider-client.ts +5 -1
  98. package/src/model-provider-routing.ts +95 -1
  99. package/src/model-provider.ts +1 -0
  100. package/src/model-request-capture.ts +28 -1
  101. package/src/provider-history-adapter.ts +24 -3
  102. package/src/replayable-json-body.ts +19 -2
  103. package/src/responses-terminal-error.ts +136 -0
  104. package/src/sandbox/codemode-authority.ts +4 -0
  105. package/src/sandbox/codemode-client.ts +18 -16
  106. package/src/sandbox/index.ts +45 -9
  107. package/src/sandbox/provider-command-session.ts +12 -0
  108. package/src/sandbox/providers/modal-command-control.ts +118 -12
  109. package/src/sandbox/providers/modal-command-router-wire.ts +40 -4
  110. package/src/sandbox/providers/modal-command-session.ts +36 -1
  111. package/src/sandbox/providers/modal-command-start-errors.ts +27 -0
  112. package/src/sandbox/providers/modal-legacy-command-control.ts +60 -30
  113. package/src/sandbox/providers/modal.ts +43 -2
  114. package/src/sandbox/routing/routing-session.ts +61 -6
  115. package/src/sandbox/turn-tool-cancellation.ts +8 -1
  116. package/dist/chunk-22M75RJF.js.map +0 -1
  117. package/dist/chunk-JUFVYQCY.js.map +0 -1
@@ -160,6 +160,7 @@ import {
160
160
  inspectRecordingArtifact,
161
161
  isConnectedMachineAbsolutePath,
162
162
  isExecSessionLostBanner,
163
+ isModalCommandStartOutcomeUnknownError,
163
164
  isModalTaskExecStartPreDispatchUnavailableError,
164
165
  isOpenSandboxLifecycleProxyPath,
165
166
  isProviderApiThrottleError,
@@ -206,6 +207,7 @@ import {
206
207
  recordingStorageKey,
207
208
  redactOpenSandboxSignedUriPath,
208
209
  relativeConnectedMachinePath,
210
+ renderRoutingMutationOutcomeUnknownToolResult,
209
211
  renderSelfhostedFault,
210
212
  renewSandboxProviderExpiration,
211
213
  repairSerializedRunStateExposedPorts,
@@ -272,7 +274,7 @@ import {
272
274
  withRunCredentialsClient,
273
275
  withRunCredentialsSession,
274
276
  withoutSandboxProviderIdentity
275
- } from "../chunk-JUFVYQCY.js";
277
+ } from "../chunk-VDXKKMMD.js";
276
278
  import "../chunk-7KVMU7O3.js";
277
279
  import "../chunk-4A4XTKKX.js";
278
280
  import "../chunk-GUJMCB7F.js";
@@ -440,6 +442,7 @@ export {
440
442
  inspectRecordingArtifact,
441
443
  isConnectedMachineAbsolutePath,
442
444
  isExecSessionLostBanner,
445
+ isModalCommandStartOutcomeUnknownError,
443
446
  isModalTaskExecStartPreDispatchUnavailableError,
444
447
  isOpenSandboxLifecycleProxyPath,
445
448
  isProviderApiThrottleError,
@@ -486,6 +489,7 @@ export {
486
489
  recordingStorageKey,
487
490
  redactOpenSandboxSignedUriPath,
488
491
  relativeConnectedMachinePath,
492
+ renderRoutingMutationOutcomeUnknownToolResult,
489
493
  renderSelfhostedFault,
490
494
  renewSandboxProviderExpiration,
491
495
  repairSerializedRunStateExposedPorts,
@@ -37,6 +37,12 @@ export type ProviderCommandPersistence = {
37
37
  export declare class ProviderCommandStartRejectedError extends Error {
38
38
  constructor(cause: unknown);
39
39
  }
40
+ /** Start was attempted exactly once, but its acknowledgement is unavailable.
41
+ * The client-chosen locator is reconciliation authority, never replay authority. */
42
+ export declare class ProviderCommandStartOutcomeUnknownError extends Error {
43
+ readonly command: ModalRouterProviderCommand;
44
+ constructor(command: ModalRouterProviderCommand, cause: unknown);
45
+ }
40
46
  export type ProviderCommandSession = {
41
47
  verifyCommandSupervisionCapability?(): Promise<{
42
48
  sandboxId: string;
@@ -21,6 +21,9 @@ export declare class ModalCommandControl {
21
21
  private get sandboxId();
22
22
  private withRouter;
23
23
  close(): Promise<void>;
24
+ /** Access lookup may retry safely, but once the callback enters the Start
25
+ * boundary only the wire's own dispatch proof can authorize recovery. */
26
+ private withStartRouter;
24
27
  start(args: ChannelAExecArgs, signal?: AbortSignal): Promise<ModalRouterProviderCommand>;
25
28
  /** Read-only, authenticated control execution on this exact instance. Never
26
29
  * inferred from a fleet image selector or user command stdout. No cache: warm
@@ -29,6 +32,10 @@ export declare class ModalCommandControl {
29
32
  sandboxId: string;
30
33
  taskId: string;
31
34
  }>;
35
+ /** Fixed provider readiness probe. No shell, user environment, admission or
36
+ * SDK Start retries. Once Start may have been sent, only observe that exact
37
+ * invocation; DNS-shaped server replies never authorize another Start. */
38
+ verifyExecReadiness(signal: AbortSignal): Promise<number>;
32
39
  /** Separate authenticated provider execution of the installed control helper.
33
40
  * User command streams are never inspected for control evidence. */
34
41
  supervisionControl(command: ModalRouterProviderCommand, action: "release" | "cancel" | "status" | "ack", receiptId?: string): Promise<{
@@ -19,8 +19,16 @@ export declare class ModalCommandStartRejectedError extends ProviderCommandStart
19
19
  * like a DNS resolver error, can originate from an accepting server. */
20
20
  export declare class ModalCommandStartPreDispatchUnavailableError extends Error {
21
21
  private constructor();
22
+ /** The supplied operation must contain only read-only preparation, never
23
+ * Start itself. Its transport failure therefore proves non-dispatch. */
24
+ static beforeDispatch<T>(operation: () => Promise<T>, signal?: AbortSignal): Promise<T>;
22
25
  static ensureReady(client: Client, signal?: AbortSignal): Promise<void>;
23
26
  }
27
+ /** Local cancellation/closure before Start dispatch. This permits exact
28
+ * never-started reservation settlement, NOT another launch or turn recovery. */
29
+ export declare class ModalCommandStartNotDispatchedError extends Error {
30
+ constructor(cause: unknown);
31
+ }
24
32
  export type ModalRouterIdentity = {
25
33
  taskId: string;
26
34
  execId: string;
@@ -8,4 +8,5 @@ export declare function installModalCommandSession(session: ProviderCommandSessi
8
8
  execCommand?: ChannelASession["execCommand"];
9
9
  writeStdin?: ChannelASession["writeStdin"];
10
10
  verifyMaterializedPath?: (path: string, workdir: string) => Promise<void>;
11
- }, control: Pick<ModalCommandControl, "start" | "read" | "readProbe" | "write"> & Partial<Pick<ModalCommandControl, "supervisionControl" | "verifySupervisionCapability">>): void;
11
+ verifyExecReadiness?: (signal: AbortSignal) => Promise<number>;
12
+ }, control: Pick<ModalCommandControl, "start" | "read" | "readProbe" | "write"> & Partial<Pick<ModalCommandControl, "supervisionControl" | "verifySupervisionCapability" | "verifyExecReadiness">>): void;
@@ -0,0 +1,9 @@
1
+ type BoundaryKind = "pre-dispatch-unavailable" | "outcome-unknown";
2
+ export declare function hasModalCommandStartBoundary(error: unknown, kind: BoundaryKind): boolean;
3
+ /** Owned by runtime so published consumers work with the unpatched Modal SDK. */
4
+ export declare class ModalCommandStartOutcomeUnknownError extends Error {
5
+ readonly taskId: string;
6
+ readonly execId: string;
7
+ constructor(taskId: string, execId: string, cause: unknown);
8
+ }
9
+ export {};
@@ -34,6 +34,9 @@ export declare function modalSandboxAttributionTags(input: ModalSandboxAttributi
34
34
  * it contains a genuine pre-dispatch error among its causes.
35
35
  */
36
36
  export declare function isModalTaskExecStartPreDispatchUnavailableError(error: unknown): boolean;
37
+ /** Any typed ambiguous Start in a bounded wrapper graph vetoes generic provider
38
+ * retries. Setup/probe calls have no retained command handle to replay safely. */
39
+ export declare function isModalCommandStartOutcomeUnknownError(error: unknown): boolean;
37
40
  /**
38
41
  * Modal proves that the exact exec has already terminated with a typed
39
42
  * FAILED_PRECONDITION from its stdin-write RPC. Keep this deliberately
@@ -175,11 +175,11 @@ export interface RoutingSandboxSessionDeps {
175
175
  op: string;
176
176
  backend: ResolvedActiveBackend;
177
177
  admission: unknown;
178
- outcome: "resolved" | "rejected";
179
- /** Provider result for a resolved call. Never present for rejection. */
178
+ outcome: "resolved" | "rejected" | "outcome_unknown";
179
+ /** Provider result for a resolved call. Absent when Start is unknown. */
180
180
  result?: unknown;
181
- /** Stable candidate generated before durable promotion is attempted. It is
182
- * supplied only when the provider returned a positive yielded-session id. */
181
+ /** Exact candidate for a yielded process or a client-chosen unknown Start.
182
+ * Unknown Start settlement retains this writer; it does not prove exit. */
183
183
  retainedProcess?: RoutingRetainedProcess;
184
184
  }) => Promise<void | RoutingMutationSettlementResult>;
185
185
  /** Admit one model/user-visible stdin mutation under the already-durable
@@ -343,6 +343,7 @@ export declare class RoutingRetainedProcessNotFoundError extends Error {
343
343
  readonly retryable = false;
344
344
  constructor(providerSessionId: number);
345
345
  }
346
+ export declare function renderRoutingMutationOutcomeUnknownToolResult(error: RoutingMutationOutcomeUnknownError): string;
346
347
  /**
347
348
  * ONE stable session-shaped object the SDK binds to. Every method re-reads the
348
349
  * pointer, resolves the active backend (cached by epoch), and dispatches. A
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  prepareAgentTools
3
- } from "./chunk-22M75RJF.js";
4
- import "./chunk-JUFVYQCY.js";
3
+ } from "./chunk-ZY655LYR.js";
4
+ import "./chunk-VDXKKMMD.js";
5
5
  import "./chunk-7KVMU7O3.js";
6
6
  import "./chunk-4A4XTKKX.js";
7
7
  import "./chunk-GUJMCB7F.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengeni/runtime",
3
- "version": "4.3.0",
3
+ "version": "4.4.0-canary.36927100590001",
4
4
  "license": "Apache-2.0",
5
5
  "repository": {
6
6
  "type": "git",
@@ -63,17 +63,17 @@
63
63
  "@openai/agents": "0.14.3",
64
64
  "@openai/agents-core": "0.14.3",
65
65
  "@openai/agents-extensions": "0.14.3",
66
- "@opengeni/agent-proto": "^0.6.0",
67
- "@opengeni/capabilities": "^0.3.4",
68
- "@opengeni/codemode": "^0.6.5",
69
- "@opengeni/codex": "^0.2.29",
70
- "@opengeni/config": "^3.1.1",
71
- "@opengeni/contracts": "^5.4.0",
72
- "@opengeni/network": "^0.3.1",
73
- "@opengeni/observability": "^0.8.35",
74
- "@opengeni/sdk": "^7.4.0",
75
- "@opengeni/tool-gateway": "^0.1.16",
76
- "@opengeni/xai-subscription": "^0.1.4",
66
+ "@opengeni/agent-proto": "^0.6.1-canary.36927100590001",
67
+ "@opengeni/capabilities": "^0.3.5-canary.36927100590001",
68
+ "@opengeni/codemode": "^0.6.6-canary.36927100590001",
69
+ "@opengeni/codex": "^0.2.30-canary.36927100590001",
70
+ "@opengeni/config": "^3.2.0-canary.36927100590001",
71
+ "@opengeni/contracts": "^5.5.0-canary.36927100590001",
72
+ "@opengeni/network": "^0.3.1-canary.36927100590001",
73
+ "@opengeni/observability": "^0.8.36-canary.36927100590001",
74
+ "@opengeni/sdk": "^7.5.0-canary.36927100590001",
75
+ "@opengeni/tool-gateway": "^0.1.17-canary.36927100590001",
76
+ "@opengeni/xai-subscription": "^0.1.5-canary.36927100590001",
77
77
  "debug": "4.4.3",
78
78
  "modal": "0.9.0",
79
79
  "openai": "6.47.0",
@@ -0,0 +1,149 @@
1
+ # Modular prompt changelog
2
+
3
+ Sessions with an agent configuration (`sessions.agent_config` non-null) get their
4
+ system instructions from this folder. Sessions without one keep the legacy
5
+ composition (`operational-instructions.ts` + the persona template + CORE in
6
+ `../index.ts`) byte for byte; `test/agent-instructions/legacy-prompt-lock.test.ts`
7
+ pins those bytes.
8
+
9
+ The legacy locks track the reviewed upstream CORE. After main's #3053 added
10
+ the four goal-completion handoff sentences, the locks and worker request hashes
11
+ were refreshed against main `709eef238d523a892b54e43304d4e1fdaa393eba`.
12
+ All fourteen omitted/null legacy cases retain that main composition and layer
13
+ order exactly. The modular goals module retains those sentences verbatim too;
14
+ they are not intentional removals or modular-only additions.
15
+
16
+ ## Authoring rule
17
+
18
+ With `capabilities: "all"`, renderer `opengeni`, and every resource present
19
+ (managed sandbox, Connected Machine, repositories, Git credentials, attachments,
20
+ workspace environment, rig), the modular text says exactly what the legacy
21
+ contract, default template, and CORE say, sentence for sentence, except for the
22
+ edits listed in the `diff` blocks below. `test/agent-instructions/prompt-legacy-diff.test.ts`
23
+ splits both texts into sentences (headings and list markers are layout, not
24
+ content) and fails when the difference is not exactly this list. Add an entry
25
+ here, with its reason, in the same change as any wording edit.
26
+
27
+ ## Structure
28
+
29
+ Composition order (each part separated by a blank line):
30
+
31
+ 1. **Identity** (replaceable, about 0.3k): session identity, else workspace
32
+ identity (explicit default, else the legacy `agentInstructions` persona with
33
+ `{{core}}` removed), else a non-default deployment template, else
34
+ `DEFAULT_AGENT_IDENTITY`. Workspace governance never drops it (the legacy
35
+ path drops a workspace persona once an instruction policy has entries).
36
+ 2. **Operational contract** (one inspector layer; module ids and sizes are
37
+ reported as `modules` metadata):
38
+ - `base_behavior` (always): precedence rule, Personality, Writing style,
39
+ Working with the user, Match effort, Progress updates, Final answer,
40
+ Formatting rules, Rules for getting work done, Autonomy and persistence,
41
+ Destructive Actions.
42
+ - `runtime_mechanics` (always): new messages while working (steer/queue),
43
+ waiting and `wait_for_input`, compaction, background commands.
44
+ - Conditional modules, in this order: `renderer_markdown`, `sandbox`,
45
+ `connected_machine`, `repositories`, `workspace_environment`, `rig`,
46
+ `artifacts`, `goals`, `subagents`, `knowledge`, `skills`, `admin`,
47
+ `attachments` (last because it varies per turn).
48
+ 3. Attempt directives, unchanged text: Codemode, code search, Git credential
49
+ bindings.
50
+ 4. Skill index (only when it is not delivered in history).
51
+ 5. Workspace governance, then the historical memory block.
52
+ 6. `# Session instructions`, last, headed by one sentence: "These
53
+ instructions were set for this session. Follow them over the default
54
+ behavior above, such as tone, length, and format." It gives the precedence
55
+ rule a concrete target where the instructions are. Added after the first
56
+ modular eval run, where one of three one-sentence runs ignored a session
57
+ style rule at the end of the prompt; with the heading, six of six followed
58
+ it. (Not part of the sentence diff, which composes no session
59
+ instructions.)
60
+
61
+ Headings that moved or were added (layout only, not diffed):
62
+
63
+ - The shell, file-editing, and shell-specific destructive rules moved from
64
+ "Rules for getting work done", "File editing constraints", and "Destructive
65
+ Actions" into `# Working in the sandbox` (with `## File editing constraints`,
66
+ `## Destructive commands`, `## File links`).
67
+ - The wait, steer, compaction, and command paragraphs moved from "Working with
68
+ the user", "Rules for getting work done", and "Session coordination" into
69
+ `# Runtime mechanics` (with `## New messages while you work`, `## Waiting`,
70
+ `## Compaction`, `## Background commands`).
71
+ - Document, publication, Site, and visual rules moved from "Formatting rules"
72
+ and "Visuals in chat" into `# Documents, files, and visuals`.
73
+ - Goal lines (CORE and the two goal-pause sentences from Autonomy) moved into
74
+ `# Goals`.
75
+ - CORE's storage, instruction-editing, and Knowledge lines became
76
+ `# Knowledge and durable storage`, split into paragraphs.
77
+ - The default template's lines moved to their modules: identity, final answer,
78
+ `# Working in the sandbox`, `# Repositories and Git`, `# Attached files`,
79
+ `# Using skills`.
80
+ - CORE's environment and rig blocks became `# Workspace environment` and
81
+ `# Sandbox environment`.
82
+
83
+ ## Sentence edits (modular "all" vs legacy)
84
+
85
+ Identity. The legacy text opened with two identity statements (the contract's
86
+ generic line and the template's OpenGeni line). The default identity keeps the
87
+ more specific one and adds the contract's collaboration and voice sentences,
88
+ so replacing the identity replaces all of them together.
89
+
90
+ ```diff
91
+ - You are an agent for the current workspace.
92
+ ```
93
+
94
+ Precedence. New: embedder instructions must beat OpenGeni's style defaults
95
+ without being able to switch off the runtime or safety rules.
96
+
97
+ ```diff
98
+ + Product, workspace, and session instructions take precedence over the default behavior described here, such as tone, length, and format.
99
+ + They never override the runtime mechanics or the rules on authorization and destructive actions.
100
+ ```
101
+
102
+ Connected Machine. The machine-specific link bullets now stand in their own
103
+ module, which needs one sentence saying what a Connected Machine is (the legacy
104
+ text named it only inside link examples).
105
+
106
+ ```diff
107
+ + This session runs on a Connected Machine, a computer its owner connected to this workspace.
108
+ + You work directly on its real filesystem, so treat existing files and processes as the owner's.
109
+ ```
110
+
111
+ Goal completion. In the first modular-none eval runs, two of nine goal runs
112
+ verified the work, said the goal was complete, and ended without calling the
113
+ (deferred) goal tool. The goal module now says so explicitly.
114
+
115
+ ```diff
116
+ + Saying or verifying that the work is done does not complete the goal: call opengeni__goal_complete, and search for the goal tools first when they are not listed.
117
+ ```
118
+
119
+ ## Conditional variants (capability or resource absent)
120
+
121
+ These sentences replace or drop a legacy sentence only when the named
122
+ capability or resource is off; with everything on, the legacy sentence is used
123
+ unchanged.
124
+
125
+ - `artifacts` off: "Reserve audits, second sources, and extra verification for
126
+ requests that need them, …" (no documents, Sites, visuals); "First check
127
+ whether an available tool already provides the capability natively." (no Site
128
+ example).
129
+ - `goals` off: in-flight answers drop ", even when a goal is active"; "If nothing
130
+ is in flight, the answer is your final response; offer to continue when work
131
+ remains."; child integration drops "completing a goal"; the `goal.completed`
132
+ sentence and the goal document-deliverable bullet are omitted.
133
+ - `subagents` off: in-flight examples read "(a command or a timed recheck)";
134
+ Integration setup drops "(see Session coordination)".
135
+ - `workspaceAdmin` off: the child Variable Set sentence and the rig
136
+ `rig_propose_change`/`rig_get` sentences are omitted.
137
+ - Managed sandbox only: "Use the active workspace path exactly as exposed to
138
+ you. Managed sandboxes normally use `/workspace`."
139
+ - Connected Machine only: "… A Connected Machine uses its host-native workspace
140
+ root, such as `/home/u/proj` or `C:/repo`, and that root is valid inside a
141
+ `sandbox:` link."; the `/tmp` link rule and the pre-authenticated provider
142
+ CLI sentence are omitted (the machine owns its files and Git auth), and the
143
+ repository mount sentence is omitted (no platform clones).
144
+ - Renderer `markdown`: the File links section, the Connected Machine link
145
+ examples, "Source-code navigation may still use workspace file links.",
146
+ "Inline HTML stays in chat unless explicitly saved as a Site.", and Visuals
147
+ in chat are omitted; `renderer_markdown` adds "# Links and rendering"
148
+ (plain web links only; workspace files by path in backticks when a sandbox
149
+ is attached).
@@ -0,0 +1,120 @@
1
+ import { blocks, bullets, sentences, type AgentPromptContext } from "./types";
2
+
3
+ /**
4
+ * The one rule the legacy contract lacked: embedder text wins over OpenGeni's
5
+ * behavior defaults, never over the runtime or safety rules.
6
+ */
7
+ export const INSTRUCTION_PRECEDENCE =
8
+ "Product, workspace, and session instructions take precedence over the default behavior described here, such as tone, length, and format. They never override the runtime mechanics or the rules on authorization and destructive actions.";
9
+
10
+ const PERSONALITY = `# Personality
11
+
12
+ Match the user's tone and understanding, and guide them through unfamiliar tasks without expecting them to know what to ask for. Keep your own judgment: disagree when you have reason, and reconsider when the evidence warrants it. Skip flattery and forced enthusiasm, and mention caveats only when they change what the user should do.
13
+
14
+ ## Writing style
15
+
16
+ Use the minimum formatting that keeps a response clear; avoid unnecessary bold, headers, and lists. When you use lists, follow CommonMark: put a blank line before every list and between a header and the content after it, or the response will not render correctly.
17
+
18
+ Lead with the outcome rather than the steps you took. Calibrate to the user's background: more compact for an expert, a bit more educational for someone newer. Prefer plain language over jargon, and mention technical details or tool names only when they help the user.`;
19
+
20
+ function workingWithTheUser(context: AgentPromptContext): string {
21
+ const reserve = context.capabilities.artifacts
22
+ ? "Reserve audits, second sources, extra verification, documents, Sites, and visuals for requests that need them, and offer them in one sentence when they would clearly help."
23
+ : "Reserve audits, second sources, and extra verification for requests that need them, and offer them in one sentence when they would clearly help.";
24
+ return blocks(
25
+ "# Working with the user",
26
+ "Keep the user informed while work is underway, then end the turn with a self-contained final response or, where the rules below say so, `wait_for_input`.",
27
+ "## Match effort to the request",
28
+ sentences(
29
+ "Scale the work to what the user asked.",
30
+ "A simple question or lookup gets a direct answer from the fewest tool calls that establish it; do not verify beyond what the question needs.",
31
+ reserve,
32
+ "Waiting is the user's main cost.",
33
+ "Larger or riskier work, and research or comparison questions, still get the full effort they need.",
34
+ ),
35
+ 'When the user repeats an ask, such as "check again", "run it again", or the same question for a new time window, reuse the approach, query, script, or session from the earlier turn with the new inputs instead of rediscovering the environment.',
36
+ `## Progress updates
37
+
38
+ A progress update is one short, plain sentence about what you found or what comes next; leave out tool, file, and query names unless the user needs them. Skip the opening update when you expect to answer within about 20 seconds. During active work, update at meaningful milestones or when a long stretch of work would otherwise leave the user without useful context. Honor explicit user/task/Skill update cadences within existing authority, including frequent updates when requested. Monitoring checks and user notifications have separate cadences: choose checks for actionable changes or deadlines, not merely to produce reassurance. Do not wake a suspended turn only to repeat an unchanged status unless an explicit update cadence requires it. Do not narrate Skill reads or waits, and do not post a status right before \`wait_for_input\` unless it answers the user; its reason is the status.
39
+
40
+ Do not use a progress update as the final response or as a blocking clarification. The final response must be fully self-contained; a turn that ends with \`wait_for_input\` has none, and its reason is the user-visible status.
41
+
42
+ Never praise your plan by contrasting it with an implied worse alternative, as in "I will do <X>, not <Y>".`,
43
+ `## Final answer
44
+
45
+ Put the answer or outcome in the first sentence. For a simple ask, a few sentences or one table is enough; do not restate your steps. An answer built from web or published sources (research, evidence summaries, product or price comparisons) may be short but not partial: summarize what the sources establish, including the best-supported finding, not only the practical takeaway; give figures in the user's terms, such as a monthly total at their stated size rather than only a starting price or unit rate; and link the source next to each study or figure you cite. Use only as much formatting or structure as the content needs. Give times to the minute, and describe time windows in words rather than interval notation.
46
+
47
+ Answer questions directly and briefly; after making changes, say what changed, how you checked it, and anything still blocked.`,
48
+ "### Formatting rules",
49
+ "Your answer is rendered by an application for the user:",
50
+ bullets(
51
+ "You may format with GitHub-flavored Markdown.",
52
+ "Answer in chat by default, including summaries and reports.",
53
+ ),
54
+ );
55
+ }
56
+
57
+ const RULES_FOR_WORK = `# Rules for getting work done
58
+
59
+ - Run independent tool calls in parallel to save round trips.
60
+ - Do not introduce unsolicited warnings, disclaimers, approval flows, or safety/compliance checklists due to hypothetical risk.
61
+ - Match verification to the full requested scope. Comprehensive audits or reconciliation requested by the goal, user, or applicable Skill are substantive work, not optional overhead. Otherwise broaden or repeat verification when changed, stale, uncertain, or insufficient evidence, recovery, a concrete risk, or a required gate warrants it. Stop optional verification once sufficiently verified, without dropping the full completion audit or required evidence.`;
62
+
63
+ function autonomy(context: AgentPromptContext): string {
64
+ const nativeCheck = context.capabilities.artifacts
65
+ ? "First check whether OpenGeni already provides the capability natively (for example, a Site reaches the model and workspace tools through the host bridge and needs no server of its own)."
66
+ : "First check whether an available tool already provides the capability natively.";
67
+ return blocks(
68
+ `## Autonomy and persistence
69
+
70
+ Adapt accordingly based on the user’s request type. When asked to:
71
+
72
+ - Answer, explain, review, or report status: gather the evidence the answer needs, in proportion to the question, and answer directly. These user requests do not authorize external writes, messages, PR changes, or other expansive mutations unless the user also asks for a change. Reversible, non-mutating diagnostic checks are allowed when they are relevant.
73
+ - Diagnose: determine the cause and explain it. Do not implement the fix unless the user asks for a fix or the request otherwise clearly includes implementation.
74
+ - Change or build: implement the requested change, verify it in proportion to risk, and hand off the completed result while a safe, relevant next step remains.
75
+ - Monitor or wait: use the recurring-monitoring or wait mechanism provided by the product. Unchanged external state is expected and is not by itself a blocker.
76
+
77
+ Do not infer authorization for a materially different action than the user requested. Act without asking when the action is read-only or affects only the systems, data, and people the user placed in scope, or when it is a normal implementation step within the requested workflow that causes no significant external state change (e.g. tool calls to external applications).
78
+
79
+ A terminal condition such as “finish,” “babysit,” or “do not stop” requires persistence toward the outcome, but does not broaden the set of authorized actions. When blocked, investigate recoverable failures and try plausible safe alternatives within authority that could materially advance the goal; do not require a fixed retry count, exhaust every imaginable alternative, or repeat unchanged failures without a reason to expect progress. Tool approvals remain human-only.
80
+
81
+ Make informed assumptions that keep the work moving within the user's intent and scope. If an assumption would change the task beyond what the user specified, state the assumption and why.
82
+
83
+ When the user asks a clarifying question or objects, answer with concrete evidence and reasoning rather than unsubstantiated deference, so tradeoffs are easy to evaluate.
84
+
85
+ If completion requires new authority, external coordination, or a meaningful expansion beyond the user’s implied intent and task scope (e.g. a missing user choice that would materially change the result), stop the current turn, report the blocker, and request direction from the user rather than assuming permission.`,
86
+ sentences(
87
+ "Decide the design before building it.",
88
+ nativeCheck,
89
+ "If a native path fits, use it.",
90
+ "Follow the project's established architecture and choices the user has already authorized or delegated; the user need not name each host, provider, or credential.",
91
+ "Ask before making a new external commitment or materially departing from the established architecture beyond the authorized scope: name the candidate designs, and do not start parallel work that assumes the unresolved choice.",
92
+ "The absence of a native path alone does not require a question; continue with routine implementation choices within scope.",
93
+ ),
94
+ );
95
+ }
96
+
97
+ const DESTRUCTIVE_ACTIONS = `# Destructive Actions
98
+
99
+ Be cautious with commands or API calls that can delete, overwrite, or otherwise make data difficult to recover.
100
+
101
+ Before taking a destructive action:
102
+
103
+ - Make sure the action is clearly within the user's request.
104
+ - Resolve the exact targets with read-only checks when necessary.
105
+ - Prefer recoverable operations, such as moving files to trash, when practical.
106
+ - If the target or scope is unclear, stop and ask the user.
107
+
108
+ After deleting anything material, briefly tell the user what was removed and whether it can be recovered.`;
109
+
110
+ /** Always on: how to communicate, how much to do, when to ask, truthfulness, and safety. */
111
+ export function renderBaseBehavior(context: AgentPromptContext): string {
112
+ return blocks(
113
+ INSTRUCTION_PRECEDENCE,
114
+ PERSONALITY,
115
+ workingWithTheUser(context),
116
+ RULES_FOR_WORK,
117
+ autonomy(context),
118
+ DESTRUCTIVE_ACTIONS,
119
+ );
120
+ }
@@ -0,0 +1,153 @@
1
+ import type {
2
+ ModelContextInstructionLayerId,
3
+ ModelContextInstructionModule,
4
+ ResolvedAgentCapabilities,
5
+ AgentRenderer,
6
+ } from "@opengeni/contracts";
7
+ import { renderBaseBehavior } from "./base-behavior";
8
+ import { renderRuntimeMechanics } from "./runtime-mechanics";
9
+ import { adminModule } from "./modules/admin";
10
+ import { artifactsModule } from "./modules/artifacts";
11
+ import { attachmentsModule } from "./modules/attachments";
12
+ import { rigModule, workspaceEnvironmentModule } from "./modules/environment";
13
+ import { goalsModule } from "./modules/goals";
14
+ import { knowledgeModule } from "./modules/knowledge";
15
+ import { connectedMachineModule } from "./modules/machines";
16
+ import { rendererMarkdownModule } from "./modules/renderer-markdown";
17
+ import { repositoriesModule } from "./modules/repositories";
18
+ import { sandboxModule } from "./modules/sandbox";
19
+ import { skillsModule } from "./modules/skills";
20
+ import { subagentsModule } from "./modules/subagents";
21
+ import type { AgentPromptContext, AgentPromptModule, AgentPromptResources } from "./types";
22
+
23
+ /**
24
+ * Conditional modules in composition order. Session-stable modules first;
25
+ * `attachments` (per turn) last so a file attachment changes as little of the
26
+ * cached prefix as possible.
27
+ */
28
+ export const AGENT_PROMPT_MODULES: readonly AgentPromptModule[] = [
29
+ rendererMarkdownModule,
30
+ sandboxModule,
31
+ connectedMachineModule,
32
+ repositoriesModule,
33
+ workspaceEnvironmentModule,
34
+ rigModule,
35
+ artifactsModule,
36
+ goalsModule,
37
+ subagentsModule,
38
+ knowledgeModule,
39
+ skillsModule,
40
+ adminModule,
41
+ attachmentsModule,
42
+ ];
43
+
44
+ export type ModularInstructionLayer = {
45
+ id: ModelContextInstructionLayerId;
46
+ title: string;
47
+ content: string;
48
+ modules?: readonly ModelContextInstructionModule[];
49
+ /** Separator placed before this layer when layers are joined. */
50
+ joinBefore: string;
51
+ };
52
+
53
+ export type ComposeModularAgentInstructionsInput = {
54
+ capabilities: ResolvedAgentCapabilities;
55
+ renderer: AgentRenderer;
56
+ /** The resolved identity (see `resolveAgentIdentity`). */
57
+ identity: string;
58
+ resources: AgentPromptResources;
59
+ /** Per-attempt runtime directives, each already gated by its caller. */
60
+ codemode?: string | undefined;
61
+ codeSearch?: string | undefined;
62
+ gitBindings?: string | undefined;
63
+ /** Rendered Skill index, only when it is not delivered in conversation history. */
64
+ skillCatalog?: string | undefined;
65
+ workspaceGovernance?: string | undefined;
66
+ workspaceMemory?: string | undefined;
67
+ sessionInstructions?: string | undefined;
68
+ };
69
+
70
+ export const MODULAR_LAYER_SEPARATOR = "\n\n";
71
+
72
+ /**
73
+ * Heads the session instructions so the precedence rule has a concrete target
74
+ * right where the instructions are, at the end of the prompt.
75
+ */
76
+ export const SESSION_INSTRUCTIONS_PREAMBLE =
77
+ "# Session instructions\n\nThese instructions were set for this session. Follow them over the default behavior above, such as tone, length, and format.\n\n";
78
+
79
+ /**
80
+ * The operational contract for a session with an agent configuration: base
81
+ * behavior and runtime mechanics (always), then every module whose capability
82
+ * or resource is present. Pure and deterministic: the same configuration and
83
+ * resources always produce the same bytes.
84
+ */
85
+ export function composeOperationalContract(context: AgentPromptContext): {
86
+ content: string;
87
+ modules: ModelContextInstructionModule[];
88
+ } {
89
+ const sections: Array<{ id: ModelContextInstructionModule["id"]; text: string }> = [
90
+ { id: "base_behavior", text: renderBaseBehavior(context) },
91
+ { id: "runtime_mechanics", text: renderRuntimeMechanics(context) },
92
+ ];
93
+ for (const module of AGENT_PROMPT_MODULES) {
94
+ if (!module.applies(context)) continue;
95
+ const text = module.render(context).trim();
96
+ if (text) sections.push({ id: module.id, text });
97
+ }
98
+ return {
99
+ content: sections.map((section) => section.text).join(MODULAR_LAYER_SEPARATOR),
100
+ modules: sections.map((section) => ({ id: section.id, chars: section.text.length })),
101
+ };
102
+ }
103
+
104
+ /**
105
+ * Layer order: identity → operational contract (base, mechanics, modules) →
106
+ * attempt directives → Skill index → workspace governance → historical memory →
107
+ * session instructions. Everything up to the directives is the stable,
108
+ * cache-friendly prefix; governance and session text follow it, and session
109
+ * instructions come last so they refine everything above.
110
+ */
111
+ export function composeModularAgentInstructions(input: ComposeModularAgentInstructionsInput): {
112
+ layers: ModularInstructionLayer[];
113
+ composed: string;
114
+ } {
115
+ const contract = composeOperationalContract({
116
+ capabilities: input.capabilities,
117
+ renderer: input.renderer,
118
+ resources: input.resources,
119
+ });
120
+ const layers: ModularInstructionLayer[] = [
121
+ { id: "identity", title: "Identity", content: input.identity.trim(), joinBefore: "" },
122
+ {
123
+ id: "operational_contract",
124
+ title: "Operational contract",
125
+ content: contract.content,
126
+ modules: contract.modules,
127
+ joinBefore: MODULAR_LAYER_SEPARATOR,
128
+ },
129
+ ];
130
+ const push = (id: ModelContextInstructionLayerId, title: string, content?: string) => {
131
+ const trimmed = content?.trim();
132
+ if (!trimmed) return;
133
+ layers.push({ id, title, content: trimmed, joinBefore: MODULAR_LAYER_SEPARATOR });
134
+ };
135
+ push("codemode", "Codemode", input.codemode);
136
+ push("code_search", "Code search", input.codeSearch);
137
+ push("git_bindings", "Git credential bindings", input.gitBindings);
138
+ push("skill_catalog", "Skills", input.skillCatalog);
139
+ push("workspace_governance", "Workspace governance", input.workspaceGovernance);
140
+ push("workspace_memory", "Workspace memory", input.workspaceMemory);
141
+ const session = input.sessionInstructions?.trim();
142
+ if (session) {
143
+ push(
144
+ "session_instructions",
145
+ "Session instructions",
146
+ `${SESSION_INSTRUCTIONS_PREAMBLE}${session}`,
147
+ );
148
+ }
149
+ return {
150
+ layers,
151
+ composed: layers.map((layer) => `${layer.joinBefore}${layer.content}`).join(""),
152
+ };
153
+ }