@ours.network/fleet 1.1.3 → 1.1.5

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.
@@ -75,7 +75,7 @@ export interface CoworkAdapter {
75
75
  }): Promise<CoworkRoleBriefingInfo>;
76
76
  setRoleCommands(roomId: string, opts: {
77
77
  role: string;
78
- commands: Array<'list-members' | 'remove-member'>;
78
+ commands: Array<'*' | 'list-members' | 'remove-member'>;
79
79
  }): Promise<void>;
80
80
  getHistory(roomId: string, opts?: {
81
81
  after?: number;
@@ -26,6 +26,12 @@ export declare const CODEX_DISABLE_INHERITED_MCP_ENV = "OURS_FLEET_CODEX_DISABLE
26
26
  /** Server-generated typed provenance followed by the exact human-authored body. */
27
27
  export declare function promptContentBlocks(text: string, origin?: PromptOrigin): acp.ContentBlock[];
28
28
  export declare function runtimeSelector(options: acp.SessionConfigOption[] | null | undefined, category: string): RuntimeSelectorMetadata | undefined;
29
+ /** Fresh ACP response including the legacy model report still used by some adapters. */
30
+ export type AcpStartupSessionResponse = acp.NewSessionResponse & {
31
+ models?: {
32
+ currentModelId?: string;
33
+ };
34
+ };
29
35
  export interface AcpSessionOptions {
30
36
  /** Opt-in Fleet watchdog, owned by this ACP session, never a process restart. */
31
37
  stallRecovery?: {
@@ -39,11 +45,20 @@ export interface AcpSessionOptions {
39
45
  argv: string[];
40
46
  cwd: string;
41
47
  env: Record<string, string>;
48
+ /** Merge the parent environment before env; false uses only the supplied env. Defaults to true. */
49
+ inheritEnvironment?: boolean;
42
50
  stateDir: string;
43
51
  mode: 'fresh' | 'resume';
44
52
  permissions: CommonPermissions;
45
53
  /** Native permission-mode id to request via session/set_mode; undefined keeps the agent default. */
46
54
  modeId?: string;
55
+ /** Require modeId to be advertised and session/set_mode to succeed before readiness. */
56
+ requireMode?: boolean;
57
+ /**
58
+ * Validate initialize and session/new reports before persistence or readiness.
59
+ * Throw/reject to fail startup. Not called for session/load or session/resume.
60
+ */
61
+ validateStartupResponse?: (initialized: acp.InitializeResponse, created: AcpStartupSessionResponse) => void | Promise<void>;
47
62
  /** Ordered explicit Brain choices that must be applied before readiness. */
48
63
  configSelections?: Array<{
49
64
  configId: string;
@@ -332,7 +332,7 @@ export class AcpSession {
332
332
  // Obsolete ours-mcp lifecycle flags are presence-sensitive. The shared
333
333
  // daemon remains operator-owned; managed ACP children are clients only.
334
334
  const childEnv = {
335
- ...process.env,
335
+ ...(options.inheritEnvironment === false ? {} : process.env),
336
336
  ...options.env,
337
337
  };
338
338
  if (options.scrubObsoleteOursAutostart)
@@ -1067,6 +1067,7 @@ export class AcpSession {
1067
1067
  ? readFileSync(this.sessionFile, 'utf8').trim()
1068
1068
  : '';
1069
1069
  let advertisedConfigOptions;
1070
+ let advertisedModes;
1070
1071
  let advertisedModelId;
1071
1072
  if (persisted && this.agentCapabilities?.sessionCapabilities?.resume != null) {
1072
1073
  const resumed = await this.connection.agent.request(acp.methods.agent.session.resume, {
@@ -1075,6 +1076,7 @@ export class AcpSession {
1075
1076
  mcpServers: this.declaredMcpServers(),
1076
1077
  });
1077
1078
  advertisedConfigOptions = resumed.configOptions;
1079
+ advertisedModes = resumed.modes;
1078
1080
  advertisedModelId = resumed.models?.currentModelId;
1079
1081
  this.captureRuntimeMetadata(advertisedConfigOptions, advertisedModelId);
1080
1082
  this.sessionId = persisted;
@@ -1090,6 +1092,7 @@ export class AcpSession {
1090
1092
  mcpServers: this.declaredMcpServers(),
1091
1093
  });
1092
1094
  advertisedConfigOptions = loaded.configOptions;
1095
+ advertisedModes = loaded.modes;
1093
1096
  advertisedModelId = loaded.models?.currentModelId;
1094
1097
  this.captureRuntimeMetadata(advertisedConfigOptions, advertisedModelId);
1095
1098
  }
@@ -1105,6 +1108,8 @@ export class AcpSession {
1105
1108
  ...(this.options.sessionMeta ? { _meta: this.options.sessionMeta } : {}),
1106
1109
  });
1107
1110
  this.sessionId = created.sessionId;
1111
+ await this.options.validateStartupResponse?.(initialized, created);
1112
+ advertisedModes = created.modes;
1108
1113
  advertisedConfigOptions = created.configOptions;
1109
1114
  advertisedModelId = created.models?.currentModelId;
1110
1115
  this.captureRuntimeMetadata(advertisedConfigOptions, advertisedModelId);
@@ -1130,14 +1135,18 @@ export class AcpSession {
1130
1135
  if (applied?.currentValue !== selection.value)
1131
1136
  throw new Error(`ACP agent did not apply required session config option '${selection.configId}' value '${selection.value}'`);
1132
1137
  }
1133
- // Do not persist a session until every required Brain choice is live. A
1134
- // failed startup closes the ACP session; persisting its id first would make
1135
- // a later resume repeatedly target that invalid session.
1136
- writeFileSync(this.sessionFile, this.sessionId + '\n', { mode: 0o600 });
1138
+ if (this.options.requireMode) {
1139
+ if (!this.options.modeId?.trim())
1140
+ throw new Error('ACP required session mode requires a non-empty modeId');
1141
+ if (!advertisedModes?.availableModes.some(mode => mode.id === this.options.modeId))
1142
+ throw new Error(`ACP agent did not advertise required session mode '${this.options.modeId}'`);
1143
+ }
1144
+ // Preserve existing persistence timing for optional permission modes.
1145
+ if (!this.options.requireMode)
1146
+ writeFileSync(this.sessionFile, this.sessionId + '\n', { mode: 0o600 });
1137
1147
  // Deliver the configured permission mode whichever way the session came up
1138
1148
  // (new, resume or load) — the launch flag never reaches an ACP agent. A
1139
- // refusal is loud but never fatal: the session then simply runs at the
1140
- // agent's own default.
1149
+ // refusal is fatal only when the adapter explicitly requires this mode.
1141
1150
  if (this.options.modeId) {
1142
1151
  try {
1143
1152
  await this.connection.agent.request(acp.methods.agent.session.setMode, {
@@ -1146,10 +1155,16 @@ export class AcpSession {
1146
1155
  });
1147
1156
  }
1148
1157
  catch (e) {
1158
+ if (this.options.requireMode)
1159
+ throw new Error(`ACP agent refused required session mode '${this.options.modeId}': `
1160
+ + (e instanceof Error ? e.message : String(e)));
1149
1161
  this.options.log(`[${this.options.name}] acp: session/set_mode "${this.options.modeId}" failed ` +
1150
1162
  `(${e instanceof Error ? e.message : String(e)}) — session runs at the agent default permission mode`);
1151
1163
  }
1152
1164
  }
1165
+ // A required mode must succeed before its session becomes resumable.
1166
+ if (this.options.requireMode)
1167
+ writeFileSync(this.sessionFile, this.sessionId + '\n', { mode: 0o600 });
1153
1168
  this.readiness = 'idle';
1154
1169
  this.events.emit('state', { status: 'idle', text: `ACP session ${this.sessionId}` });
1155
1170
  this.conversation.appendSafe({
package/dist/spawn.d.ts CHANGED
@@ -5,6 +5,7 @@ import { type OpsDeps } from './ops.js';
5
5
  import { type CreationDeps, type CreationProvenance } from './creation.js';
6
6
  import './harness/claude-code.js';
7
7
  import './harness/codex.js';
8
+ import './harness/hermes.js';
8
9
  import { type SupervisorLauncher } from './temp-lifecycle.js';
9
10
  /**
10
11
  * The provenance record written by the most recent spawn in this process, so
package/dist/spawn.js CHANGED
@@ -12,6 +12,7 @@ import { VERSION } from './version.js';
12
12
  import { recordGeneratedAgentSource } from './generated-agent-source.js';
13
13
  import './harness/claude-code.js';
14
14
  import './harness/codex.js';
15
+ import './harness/hermes.js';
15
16
  import { archiveTempState, makeTempSupervisorLauncher, prepareTempSupervisor, reclaimStaleTempState, } from './temp-lifecycle.js';
16
17
  /**
17
18
  * The provenance record written by the most recent spawn in this process, so
@@ -0,0 +1,5 @@
1
+ # Illustrative model ID: choose one supported by the provider provisioned in
2
+ # the stopped role's Hermes home. This is not a Fleet default model.
3
+ harness: hermes
4
+ session: acp
5
+ model: gpt-5.6-sol
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ours.network/fleet",
3
- "version": "1.1.3",
3
+ "version": "1.1.5",
4
4
  "description": "Harness-agnostic fleet of persistent, identity-bound AI agents. Declarative fleet.yaml, managed native/ACP sessions, supervision, and ours.network messaging.",
5
5
  "type": "module",
6
6
  "license": "FSL-1.1-Apache-2.0",