@tangle-network/agent-interface 0.31.0 → 0.32.0

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.
package/README.md CHANGED
@@ -62,6 +62,13 @@ const provider: AgentEnvironmentProvider = {
62
62
  };
63
63
  ```
64
64
 
65
+ ## Exact process environments
66
+
67
+ Providers may expose the optional `exactProcess` capability for isolated, reproducible process execution.
68
+ It is separate from agent-backed `create()` because it guarantees a fresh environment, immutable image identity, explicit resources, bounded exact-byte file reads, shell-free argv, replacement process environment, recoverable output and terminal reason, bounded network access, and collision-safe idempotent recovery without starting a provider-managed agent.
69
+ Higher-level runtimes can use this primitive for measured candidates without making candidate lifecycle part of the provider contract.
70
+ Providers must omit the capability unless every property is enforced on their real execution path.
71
+
65
72
  ## Frozen improvement candidates
66
73
 
67
74
  `AgentCandidateBundle` is the portable output of an improvement run: a recursively strict profile, an explicit disabled/no-op/changed code result, a shell-free launch, optional knowledge, isolated memory, ancestry, and spend.
@@ -174,6 +174,16 @@ export const agentCandidateExecutionSchema = z
174
174
  })
175
175
  .strict()
176
176
  .superRefine((execution, ctx) => {
177
+ const requiresPath = execution.launch.kind === "container-command"
178
+ ? !execution.launch.executable.startsWith("/")
179
+ : execution.launch.interpreter !== undefined;
180
+ if (requiresPath && !execution.env?.PATH?.value.trim()) {
181
+ ctx.addIssue({
182
+ code: "custom",
183
+ path: ["env", "PATH"],
184
+ message: "relative candidate executables require an explicit public PATH",
185
+ });
186
+ }
177
187
  if (execution.env?.TANGLE_CANDIDATE_TASK_PATH !== undefined) {
178
188
  ctx.addIssue({
179
189
  code: "custom",
@@ -320,6 +320,13 @@ export const agentCandidateExecutionPlanMaterialSchema = z
320
320
  })
321
321
  .strict()
322
322
  .superRefine((material, ctx) => {
323
+ if (!material.launch.executable.startsWith("/") && !material.launch.env.PATH?.value.trim()) {
324
+ ctx.addIssue({
325
+ code: "custom",
326
+ path: ["launch", "env", "PATH"],
327
+ message: "relative execution-plan executables require an explicit public PATH",
328
+ });
329
+ }
323
330
  const routeIds = material.model.routes.map((route) => route.kind === "mode" || route.kind === "subagent"
324
331
  ? `${route.kind}:${route.name}`
325
332
  : route.kind);
@@ -71,6 +71,10 @@ export declare function candidateFixture(): {
71
71
  kind: "public";
72
72
  value: string;
73
73
  };
74
+ PATH: {
75
+ kind: "public";
76
+ value: string;
77
+ };
74
78
  };
75
79
  environment: {
76
80
  kind: "pinned-container";
@@ -62,6 +62,7 @@ export function candidateFixture() {
62
62
  cwd: { workspace: "candidate", path: "." },
63
63
  env: {
64
64
  NODE_ENV: { kind: "public", value: "production" },
65
+ PATH: { kind: "public", value: "/usr/local/bin:/usr/bin:/bin" },
65
66
  },
66
67
  environment: {
67
68
  kind: "pinned-container",
@@ -1,4 +1,5 @@
1
1
  import type { AgentProfile, AgentProfileCapabilities, AgentProfileValidationResult } from "./agent-profile.js";
2
+ import type { AgentCandidateTermination } from "./agent-candidate.js";
2
3
  import type { InputPart, StreamEvent, TokenUsage } from "./index.js";
3
4
  /** Portable profile reference: inline profile or provider catalog id. */
4
5
  export type AgentProfileRef = AgentProfile | string;
@@ -48,6 +49,128 @@ export interface ExecResult {
48
49
  stdout: string;
49
50
  stderr: string;
50
51
  }
52
+ export type AgentExactProcessEgressMode = "blocked" | "strict";
53
+ /**
54
+ * Outbound network policy for an exact process environment. `blocked` denies
55
+ * every protocol. `strict` permits only the named domains; direct-address,
56
+ * alternate-protocol, and cross-environment bypasses must fail.
57
+ */
58
+ export type AgentExactProcessEgressPolicy = {
59
+ mode: "blocked";
60
+ } | {
61
+ mode: "strict";
62
+ allowDomains: readonly string[];
63
+ };
64
+ /** Explicit portable limits for an exact process environment. */
65
+ export interface AgentExactProcessResources {
66
+ /** Positive CPU core count. */
67
+ cpu: number;
68
+ /** Positive integer mebibytes of memory. */
69
+ memoryMb: number;
70
+ /** Positive integer mebibytes of disk. */
71
+ diskMb: number;
72
+ }
73
+ /** Terminal or running state reported by an exact process host. */
74
+ export interface AgentExactProcessStatus {
75
+ pid: number;
76
+ running: boolean;
77
+ /** -1 while running; the exact process exit code after termination. */
78
+ exitCode: number;
79
+ exitSignal?: string;
80
+ /** Required after termination; absent only while running. */
81
+ termination?: AgentCandidateTermination;
82
+ }
83
+ /** Recoverable handle for one shell-free process. */
84
+ export interface AgentExactProcess {
85
+ readonly pid: number;
86
+ status(): Promise<AgentExactProcessStatus>;
87
+ wait(): Promise<AgentCandidateTermination>;
88
+ /** Force-stop the full process tree. Idempotent after the process exits. */
89
+ kill(): Promise<void>;
90
+ /** Each iteration replays buffered UTF-8 stdout, then continues until exit. */
91
+ stdout(): AsyncIterable<string>;
92
+ /** Each iteration replays buffered UTF-8 stderr, then continues until exit. */
93
+ stderr(): AsyncIterable<string>;
94
+ }
95
+ /** Shell-free launch whose environment replaces, rather than extends, ambient variables. */
96
+ export interface AgentExactProcessLaunch {
97
+ /** Absolute path unless {@link env} supplies an explicit `PATH`. */
98
+ executable: string;
99
+ args: readonly string[];
100
+ cwd: string;
101
+ env: Readonly<Record<string, string>>;
102
+ stdin?: string;
103
+ /** Positive integer milliseconds, or zero to disable the process timeout. */
104
+ timeoutMs: number;
105
+ }
106
+ export interface AgentExactProcessManager {
107
+ list(): Promise<AgentExactProcessStatus[]>;
108
+ get(pid: number): Promise<AgentExactProcess | null>;
109
+ /** Providers must honor the abort signal when supplied. */
110
+ spawn(input: AgentExactProcessLaunch, options?: {
111
+ signal?: AbortSignal;
112
+ }): Promise<AgentExactProcess>;
113
+ }
114
+ /**
115
+ * Fresh environment with no provider-managed user workload.
116
+ *
117
+ * Authenticated provider control services may exist, but no customer workload
118
+ * ingress or provider-managed user process may exist. The launched process
119
+ * sees only its supplied environment variables, with no ambient or injected
120
+ * secrets.
121
+ */
122
+ export interface AgentExactProcessEnvironment {
123
+ readonly id: string;
124
+ readonly provider: string;
125
+ readonly metadata?: Record<string, unknown>;
126
+ readonly process: AgentExactProcessManager;
127
+ /** Write exact bytes to an absolute path with a POSIX mode from 0 through 07777. Providers must honor the abort signal when supplied. */
128
+ writeFile(path: string, bytes: Uint8Array, options: {
129
+ mode: number;
130
+ signal?: AbortSignal;
131
+ }): Promise<void>;
132
+ /** Read exact bytes or fail before content is loaded when the file exceeds maxBytes. */
133
+ readFile(path: string, options: {
134
+ maxBytes: number;
135
+ signal?: AbortSignal;
136
+ }): Promise<Uint8Array>;
137
+ destroy(): Promise<void>;
138
+ }
139
+ export interface AgentExactProcessEnvironmentQuery {
140
+ /** Every supplied key/value must match persisted environment metadata exactly. */
141
+ metadata?: Record<string, unknown>;
142
+ providerOptions?: Record<string, unknown>;
143
+ }
144
+ /** Input for a fresh environment with no provider-managed agent process. */
145
+ export interface CreateAgentExactProcessEnvironmentInput {
146
+ /** Provider-specific immutable image reference. */
147
+ image: string;
148
+ egress: AgentExactProcessEgressPolicy;
149
+ /** Positive integer milliseconds. */
150
+ maxLifetimeMs: number;
151
+ /** Positive integer milliseconds when supplied. */
152
+ provisionTimeoutMs?: number;
153
+ /** Required limits; exact execution never inherits provider defaults. */
154
+ resources: AgentExactProcessResources;
155
+ metadata: Record<string, unknown>;
156
+ idempotencyKey: string;
157
+ signal?: AbortSignal;
158
+ /** Provider-native fields may narrow, but never weaken, the isolation contract. */
159
+ providerOptions?: Record<string, unknown>;
160
+ }
161
+ /** Optional all-or-nothing exact process capability of an environment provider. */
162
+ export interface AgentExactProcessProvider {
163
+ /**
164
+ * Repeating the same idempotency key and input returns the same environment.
165
+ * Reusing the key with any different create input must fail.
166
+ * Unsupported egress modes must fail instead of weakening the policy.
167
+ */
168
+ create(input: CreateAgentExactProcessEnvironmentInput): Promise<AgentExactProcessEnvironment>;
169
+ /** Ordinary environments must return null. */
170
+ get(id: string): Promise<AgentExactProcessEnvironment | null>;
171
+ /** Return every matching exact environment; providers own any native pagination. */
172
+ list(query?: AgentExactProcessEnvironmentQuery): Promise<AgentExactProcessEnvironment[]>;
173
+ }
51
174
  export interface CheckpointRequest {
52
175
  name?: string;
53
176
  metadata?: Record<string, unknown>;
@@ -165,6 +288,10 @@ export interface AgentEnvironmentCapabilities {
165
288
  placement: boolean;
166
289
  usage: boolean;
167
290
  confidential: boolean;
291
+ /** Present only when {@link AgentEnvironmentProvider.exactProcess} is implemented. */
292
+ exactProcess?: {
293
+ egress: readonly AgentExactProcessEgressMode[];
294
+ };
168
295
  }
169
296
  export interface CreateAgentEnvironmentInput {
170
297
  profile: AgentProfileRef;
@@ -182,6 +309,7 @@ export interface CreateAgentEnvironmentInput {
182
309
  }
183
310
  export interface AgentEnvironmentProvider {
184
311
  readonly name: string;
312
+ readonly exactProcess?: AgentExactProcessProvider;
185
313
  capabilities(): AgentEnvironmentCapabilities | Promise<AgentEnvironmentCapabilities>;
186
314
  validateProfile?(profile: AgentProfileRef): AgentProfileValidationResult | Promise<AgentProfileValidationResult>;
187
315
  create(input: CreateAgentEnvironmentInput): Promise<AgentEnvironment>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/agent-interface",
3
- "version": "0.31.0",
3
+ "version": "0.32.0",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "license": "MIT",