sortie-dogs 0.10.8 → 0.10.9

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.
@@ -3,5 +3,5 @@
3
3
  * installed project marker without importing every asset body.
4
4
  */
5
5
  export declare const RUNTIME_ASSET_VERSION = "0.3.89-completion-proof-v1";
6
- export declare const V010_RUNTIME_ASSET_VERSION = "0.10.0-v0912-language4-cost-rpt10-compaction-ref2-proposal1-review-remediation1-surface4-proposal-recovery2-quality1-terminal2-route1-path1-permission2-prerequisite1";
6
+ export declare const V010_RUNTIME_ASSET_VERSION = "0.10.0-v0912-language4-cost-rpt10-compaction-ref2-proposal1-review-remediation1-surface4-proposal-recovery2-quality1-terminal2-route1-path1-permission2-prerequisite1-v2-bridge1-intent1";
7
7
  export type RuntimeAssetVersion = typeof RUNTIME_ASSET_VERSION | typeof V010_RUNTIME_ASSET_VERSION;
@@ -3,4 +3,4 @@
3
3
  * installed project marker without importing every asset body.
4
4
  */
5
5
  export const RUNTIME_ASSET_VERSION = "0.3.89-completion-proof-v1";
6
- export const V010_RUNTIME_ASSET_VERSION = "0.10.0-v0912-language4-cost-rpt10-compaction-ref2-proposal1-review-remediation1-surface4-proposal-recovery2-quality1-terminal2-route1-path1-permission2-prerequisite1";
6
+ export const V010_RUNTIME_ASSET_VERSION = "0.10.0-v0912-language4-cost-rpt10-compaction-ref2-proposal1-review-remediation1-surface4-proposal-recovery2-quality1-terminal2-route1-path1-permission2-prerequisite1-v2-bridge1-intent1";
@@ -1088,6 +1088,11 @@ export function createProfiledPlugin(profile, assetVersion) {
1088
1088
  const proposal = who.role === "dog-operator" ? await proposals.read(root) : undefined;
1089
1089
  const proposalChild = proposal?.phase === "investigating" && proposal.proposal_session_id === request.sessionID;
1090
1090
  if (proposalChild) {
1091
+ // OpenCode V2 exposes server plugin tools to Code Mode through its `execute`
1092
+ // conduit. The nested submit tool still performs the session/grant checks below;
1093
+ // denying the conduit makes an otherwise read-only proposal impossible to submit.
1094
+ if (request.tool === "execute")
1095
+ return;
1091
1096
  if (request.tool !== "read")
1092
1097
  throw new Error("operator-proposal-readonly-role");
1093
1098
  if (typeof args.filePath !== "string")
package/dist/plugin/v2.js CHANGED
@@ -140,6 +140,8 @@ function legacyToolInput(name, input) {
140
140
  value.subagent_type = value.agent;
141
141
  if (typeof value.sessionID === "string")
142
142
  value.task_id = value.sessionID;
143
+ delete value.agent;
144
+ delete value.sessionID;
143
145
  }
144
146
  if (name === "read" && typeof value.path === "string" && value.filePath === undefined)
145
147
  value.filePath = value.path;
@@ -177,6 +179,7 @@ function assistantText(message) {
177
179
  const text = [...array(message.content)].reverse().find(part => record(part) && part.type === "text" && typeof part.text === "string" && part.text.trim().length > 0);
178
180
  return record(text) && typeof text.text === "string" ? text.text.trim() : undefined;
179
181
  }
182
+ const V2_SUBAGENT_PROMPT_PREFIX = "You are a subagent spawned by another session.\n";
180
183
  function reportPanel(rendered) {
181
184
  const summary = rendered.indexOf(RETURN_REPORT_MARKER);
182
185
  if (summary < 0)
@@ -257,12 +260,16 @@ async function registerV2Hooks(context, hooks) {
257
260
  if (!record(info) || !record(event.prompt))
258
261
  return;
259
262
  const model = modelReference(info.model) ?? { providerID: "unknown", modelID: "unknown" };
263
+ const nativeText = String(event.prompt.text ?? "");
264
+ const legacyText = typeof info.parentID === "string" && nativeText.startsWith(V2_SUBAGENT_PROMPT_PREFIX)
265
+ ? nativeText.slice(V2_SUBAGENT_PROMPT_PREFIX.length)
266
+ : nativeText;
260
267
  const output = { message: { id: String(event.messageID ?? ""), agent: string(info.agent), model },
261
- parts: [{ type: "text", text: String(event.prompt.text ?? "") }] };
268
+ parts: [{ type: "text", text: legacyText }] };
262
269
  await hooks["chat.message"]({ sessionID: String(event.sessionID), messageID: String(event.messageID ?? ""),
263
270
  ...(typeof info.agent === "string" ? { agent: info.agent } : {}), model }, output);
264
271
  const text = output.parts.find(part => record(part) && part.type === "text" && typeof part.text === "string");
265
- if (record(text) && typeof text.text === "string")
272
+ if (record(text) && typeof text.text === "string" && text.text !== legacyText)
266
273
  event.prompt.text = text.text;
267
274
  if (typeof output.message.agent === "string" && output.message.agent !== info.agent) {
268
275
  await context.session.switchAgent({ sessionID: event.sessionID, agent: output.message.agent });
@@ -145,6 +145,12 @@ For a nontrivial request whose source facts, unit boundaries, or exact validatio
145
145
  do not author a giant speculative plan at the root. Freeze the original request once with stable ordered requirement IDs,
146
146
  including every negative and quality condition, authoritative references, finite proposal read/submission budgets, and the
147
147
  maximum read prefixes. Call ${profile.toolPrefix}begin_operator_proposal. Dispatch its exact ${operator} Task unchanged.
148
+ The begin intent_json has exactly this shape (proposal_budget is the only optional field):
149
+ {"schema_version":"0.1","original_request":{"text":"complete original user request verbatim","source_ref":"user:message-id"},"requirements":[{"id":"R1","text":"exact ordered requirement","kind":"requirement"}],"authoritative_refs":["user:message-id"],"allow_read":["existing/path"]}
150
+ Every requirement entry must include kind="requirement" | "negative" | "quality". Copy original_request.text byte-for-byte.
151
+ Use only authoritative_refs and allow_read for begin intent scope. project_root, source_refs, max_read_prefixes,
152
+ authoritative_references, and other aliases are invalid. Do not guess a smaller contract after rejection: use this exact
153
+ shape, preserving all ordered requirements, and omit proposal_budget only when the deterministic host default is intended.
148
154
  Freeze product requirements from the original request, not extra implementation criteria invented from workflow
149
155
  bookkeeping. Keep proposal read/submission allowances in proposal_budget and host counters; do not turn spent
150
156
  budgets or your own reporting obligations into worker validation commands. Preserve any explicit user requirement.
@@ -336,6 +342,9 @@ When the prompt starts SORTIE_OPERATOR_PROPOSAL or the host supplies SORTIE_PROP
336
342
  perform only its bounded read investigation and submit the complete packet
337
343
  through ${profile.toolPrefix}submit_operator_proposal before returning; a prose-only return is forbidden. If it returns invalid-proposal,
338
344
  repair only the named code (using actual_reads for a budget estimate mismatch) within the finite submission budget.
345
+ When OpenCode V2 exposes that profile tool through Code Mode, use the execute conduit only to call
346
+ ${profile.toolPrefix}submit_operator_proposal. Do not use execute for HTTP, another tool, filesystem access, or computation
347
+ that bypasses this bounded proposal contract. Source inspection remains on the native Read tool.
339
348
  A successful status=submitted ends this investigation Task: return to the parent without further tools, even if a generic
340
349
  continuation asks for the next step. Submission is not execution admission; never call operator_next or dispatch a worker.
341
350
  For an admitted execution queue, call
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sortie-dogs",
3
- "version": "0.10.8",
3
+ "version": "0.10.9",
4
4
  "description": "Bounded agent harness and validated orchestration loop plugin for OpenCode",
5
5
  "keywords": [
6
6
  "opencode",