snipara-companion 3.2.13 → 3.2.15
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 +22 -0
- package/dist/index.d.ts +19 -1
- package/dist/index.js +278 -1
- package/docs/FULL_REFERENCE.md +36 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -22,6 +22,27 @@ npx -y snipara-companion impact src/auth/session.ts --source local
|
|
|
22
22
|
npx -y snipara-companion source init .
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
+
## Companion Continuity Contract V1
|
|
26
|
+
|
|
27
|
+
Editor integrations and post-activation workflows can ask Companion for one
|
|
28
|
+
machine-readable "continue this workspace" payload:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npx -y snipara-companion@latest continue-workspace --include-session-context --json
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
The payload version is `snipara.companion.continuity.v1`. It is designed for
|
|
35
|
+
native editor commands, status bars, panels, and agent handoffs that need to
|
|
36
|
+
resume real work without rescanning or reimplementing Snipara semantics. It
|
|
37
|
+
includes project binding, session bootstrap entries and quality warnings,
|
|
38
|
+
workflow phase state, Team Sync handoff summary, passive source snapshot status,
|
|
39
|
+
session snapshot summary, stable local artifact paths, and recommended next
|
|
40
|
+
actions.
|
|
41
|
+
|
|
42
|
+
Use this after `create-snipara` activation. `create-snipara` remains the
|
|
43
|
+
canonical engine for first workspace setup; Companion owns the repeatable local
|
|
44
|
+
continuity loop after that.
|
|
45
|
+
|
|
25
46
|
Example output excerpt:
|
|
26
47
|
|
|
27
48
|
```text
|
|
@@ -56,6 +77,7 @@ These commands are useful without hosted Snipara:
|
|
|
56
77
|
| `workflow start` / `phase-start` / `phase-commit` / `resume` | Agent continuity that survives compaction |
|
|
57
78
|
| `workflow timeline` / `workflow session` | Append-only local activity log and Session Snapshot V0 |
|
|
58
79
|
| `workflow decisions` / `workflow decide` | Local human decision requests and response receipts |
|
|
80
|
+
| `run --emit-policy-decisions` | Project Policy review requests in the agent workflow |
|
|
59
81
|
| `workflow producer-triage` | Ask for human review of unreviewed Producer Loop samples |
|
|
60
82
|
| `workflow producer-report` | Local Producer Loop adoption and calibration report |
|
|
61
83
|
| `workflow producer-review` | Mark local Producer Loop samples reviewed or rejected |
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { ProjectIntentDetectionResult, ProjectPolicyDecision, ProjectRealityCheckResult, ProjectIntelligenceEngineeringLeadPlanSummary, ProjectPolicyRule, OutcomeIntelligenceCalibration, ControlledWorkerExecutionMode, ControlledWorkerExecutionReceipt } from '@snipara/project-intelligence-contracts';
|
|
2
|
+
import { ProjectIntentDetectionResult, ProjectPolicyDecision, ProjectRealityCheckResult, ProjectIntelligenceEngineeringLeadPlanSummary, ProjectPolicyRule, DecisionRequest, OutcomeIntelligenceCalibration, ControlledWorkerExecutionMode, ControlledWorkerExecutionReceipt } from '@snipara/project-intelligence-contracts';
|
|
3
3
|
|
|
4
4
|
declare function resolveQueryFromToolInput(toolInput?: string, tool?: string): string | null;
|
|
5
5
|
|
|
@@ -2073,6 +2073,14 @@ interface RealityCheckCommandOptions {
|
|
|
2073
2073
|
declare function buildLocalProjectRealityCheck(options: RealityCheckCommandOptions): ProjectRealityCheckResult;
|
|
2074
2074
|
declare function realityCheckCommand(options: RealityCheckCommandOptions): Promise<void>;
|
|
2075
2075
|
|
|
2076
|
+
interface DecisionRequestWriteResult {
|
|
2077
|
+
status: "written" | "duplicate_pending" | "duplicate_resolved";
|
|
2078
|
+
requestId: string;
|
|
2079
|
+
fingerprint: string;
|
|
2080
|
+
path?: string;
|
|
2081
|
+
relativePath?: string;
|
|
2082
|
+
}
|
|
2083
|
+
|
|
2076
2084
|
interface MemoryHealthCommandOptions {
|
|
2077
2085
|
scope?: MemoryScope;
|
|
2078
2086
|
includeInactive?: boolean;
|
|
@@ -2463,6 +2471,7 @@ interface ProjectRunCommandOptions {
|
|
|
2463
2471
|
servedJudgmentId?: string;
|
|
2464
2472
|
skipAdvisorReceipts?: boolean;
|
|
2465
2473
|
outcomeReceiptFiles?: string[];
|
|
2474
|
+
emitPolicyDecisions?: boolean;
|
|
2466
2475
|
json?: boolean;
|
|
2467
2476
|
}
|
|
2468
2477
|
interface ProjectRunGuardResult {
|
|
@@ -2509,11 +2518,20 @@ interface ProjectIntelligenceRunResult {
|
|
|
2509
2518
|
guard?: ProjectRunGuardResult;
|
|
2510
2519
|
packageReview?: ProjectRunPackageReview;
|
|
2511
2520
|
policyGates: ProjectPolicyGatesResult;
|
|
2521
|
+
policyDecisionRequests?: ProjectRunPolicyDecisionRequests;
|
|
2512
2522
|
advisorReceiptCapture?: ProjectRunAdvisorReceiptCapture;
|
|
2513
2523
|
outcomeCalibration?: OutcomeIntelligenceCalibration;
|
|
2514
2524
|
judgmentCard: ProjectIntelligenceJudgmentCard;
|
|
2515
2525
|
suggestedCommands: string[];
|
|
2516
2526
|
}
|
|
2527
|
+
interface ProjectRunPolicyDecisionRequests {
|
|
2528
|
+
version: "project-intelligence.policy-decision-requests.v1";
|
|
2529
|
+
emitted: boolean;
|
|
2530
|
+
requestCount: number;
|
|
2531
|
+
requests: DecisionRequest[];
|
|
2532
|
+
writes: DecisionRequestWriteResult[];
|
|
2533
|
+
caveats: string[];
|
|
2534
|
+
}
|
|
2517
2535
|
declare function buildProjectIntelligenceRun(options: ProjectRunCommandOptions): Promise<ProjectIntelligenceRunResult>;
|
|
2518
2536
|
declare function projectIntelligenceRunCommand(options: ProjectRunCommandOptions): Promise<void>;
|
|
2519
2537
|
|
package/dist/index.js
CHANGED
|
@@ -16928,6 +16928,7 @@ var MIN_WORKFLOW_SURFACE_TOKENS = 200;
|
|
|
16928
16928
|
var STALE_BOOTSTRAP_MEMORY_DAYS = 90;
|
|
16929
16929
|
var TASK_COMMIT_TIMEOUT_MS = 3e4;
|
|
16930
16930
|
var FINAL_COMMIT_TIMEOUT_MS = 9e4;
|
|
16931
|
+
var COMPANION_CONTINUITY_CONTRACT_VERSION = "snipara.companion.continuity.v1";
|
|
16931
16932
|
var FINAL_COMMIT_RETRY_TIMEOUT_MS = 45e3;
|
|
16932
16933
|
var FINAL_COMMIT_SUMMARY_MAX_CHARS = 1200;
|
|
16933
16934
|
var FINAL_COMMIT_RETRY_SUMMARY_MAX_CHARS = 600;
|
|
@@ -24482,6 +24483,191 @@ async function sessionBootstrapCommand(options) {
|
|
|
24482
24483
|
printSessionBootstrapQuality(buildPrintedBootstrapQuality(printedBootstrap));
|
|
24483
24484
|
}
|
|
24484
24485
|
}
|
|
24486
|
+
function emptySessionMemoriesResult() {
|
|
24487
|
+
return {
|
|
24488
|
+
critical: { memories: [], count: 0, tokens: 0 },
|
|
24489
|
+
daily: { memories: [], count: 0, tokens: 0 },
|
|
24490
|
+
total_tokens: 0
|
|
24491
|
+
};
|
|
24492
|
+
}
|
|
24493
|
+
function readLocalJsonFile(relativePath2) {
|
|
24494
|
+
const absolutePath = path20.join(process.cwd(), relativePath2);
|
|
24495
|
+
if (!fs21.existsSync(absolutePath)) {
|
|
24496
|
+
return null;
|
|
24497
|
+
}
|
|
24498
|
+
try {
|
|
24499
|
+
const parsed = JSON.parse(fs21.readFileSync(absolutePath, "utf-8"));
|
|
24500
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
24501
|
+
} catch {
|
|
24502
|
+
return null;
|
|
24503
|
+
}
|
|
24504
|
+
}
|
|
24505
|
+
function summarizeSourceSnapshot(snapshot) {
|
|
24506
|
+
if (!snapshot) {
|
|
24507
|
+
return {
|
|
24508
|
+
status: "missing",
|
|
24509
|
+
snapshotPath: path20.join(".snipara", "source", "latest.json"),
|
|
24510
|
+
nextAction: "Run snipara-companion source sync --json after meaningful local code or docs movement."
|
|
24511
|
+
};
|
|
24512
|
+
}
|
|
24513
|
+
const summary = snapshot.summary && typeof snapshot.summary === "object" && !Array.isArray(snapshot.summary) ? snapshot.summary : {};
|
|
24514
|
+
return {
|
|
24515
|
+
status: "present",
|
|
24516
|
+
snapshotPath: path20.join(".snipara", "source", "latest.json"),
|
|
24517
|
+
generatedAt: snapshot.generatedAt,
|
|
24518
|
+
revision: snapshot.revision,
|
|
24519
|
+
totalFiles: summary.totalFiles,
|
|
24520
|
+
totalBytes: summary.totalBytes,
|
|
24521
|
+
skipped: summary.skipped
|
|
24522
|
+
};
|
|
24523
|
+
}
|
|
24524
|
+
function summarizeWorkflowStateForContinuity(workflow2) {
|
|
24525
|
+
if (!workflow2) {
|
|
24526
|
+
return {
|
|
24527
|
+
status: "missing",
|
|
24528
|
+
statePath: WORKFLOW_STATE_RELATIVE_PATH,
|
|
24529
|
+
nextAction: "Run snipara-companion workflow start for multi-phase work."
|
|
24530
|
+
};
|
|
24531
|
+
}
|
|
24532
|
+
return {
|
|
24533
|
+
status: workflow2.status,
|
|
24534
|
+
workflowId: workflow2.workflowId,
|
|
24535
|
+
goal: workflow2.goal,
|
|
24536
|
+
currentPhaseId: workflow2.currentPhaseId,
|
|
24537
|
+
statePath: WORKFLOW_STATE_RELATIVE_PATH,
|
|
24538
|
+
phases: workflow2.phases.map((phase) => ({
|
|
24539
|
+
id: phase.id,
|
|
24540
|
+
title: phase.title,
|
|
24541
|
+
status: phase.status
|
|
24542
|
+
}))
|
|
24543
|
+
};
|
|
24544
|
+
}
|
|
24545
|
+
function summarizeTeamSyncForContinuity() {
|
|
24546
|
+
const state = loadTeamSyncState();
|
|
24547
|
+
const summary = buildTeamSyncSummary(state);
|
|
24548
|
+
const latestHandoff = state.handoffs.at(-1);
|
|
24549
|
+
return {
|
|
24550
|
+
statePath: path20.relative(process.cwd(), getTeamSyncStatePath()),
|
|
24551
|
+
activeWorkCount: summary.activeWorkCount,
|
|
24552
|
+
staleWorkCount: summary.staleWorkCount,
|
|
24553
|
+
completedWorkCount: summary.completedWorkCount,
|
|
24554
|
+
archivedWorkCount: summary.archivedWorkCount,
|
|
24555
|
+
handoffCount: summary.handoffCount,
|
|
24556
|
+
latestHandoff: latestHandoff ? {
|
|
24557
|
+
summary: latestHandoff.summary,
|
|
24558
|
+
next: latestHandoff.next,
|
|
24559
|
+
attention: latestHandoff.attention,
|
|
24560
|
+
createdAt: latestHandoff.createdAt
|
|
24561
|
+
} : null
|
|
24562
|
+
};
|
|
24563
|
+
}
|
|
24564
|
+
function buildCompanionContinuityPayload(args) {
|
|
24565
|
+
const config = args.configured ? loadConfig() : null;
|
|
24566
|
+
const workflow2 = readWorkflowState2();
|
|
24567
|
+
const sessionSnapshot = readSessionSnapshot();
|
|
24568
|
+
const sourceSnapshot = readLocalJsonFile(path20.join(".snipara", "source", "latest.json"));
|
|
24569
|
+
return {
|
|
24570
|
+
version: COMPANION_CONTINUITY_CONTRACT_VERSION,
|
|
24571
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
24572
|
+
configured: args.configured,
|
|
24573
|
+
project: config ? {
|
|
24574
|
+
projectId: config.projectId,
|
|
24575
|
+
apiUrl: config.apiUrl,
|
|
24576
|
+
sessionId: config.sessionId
|
|
24577
|
+
} : null,
|
|
24578
|
+
artifacts: {
|
|
24579
|
+
workflowStatePath: WORKFLOW_STATE_RELATIVE_PATH,
|
|
24580
|
+
teamSyncStatePath: path20.relative(process.cwd(), getTeamSyncStatePath()),
|
|
24581
|
+
sessionSnapshotPath: path20.join(".snipara", "activity", "session.json"),
|
|
24582
|
+
sourceSnapshotPath: path20.join(".snipara", "source", "latest.json")
|
|
24583
|
+
},
|
|
24584
|
+
bootstrap: args.bootstrap,
|
|
24585
|
+
bootstrapQuality: args.bootstrapQuality,
|
|
24586
|
+
localWarmSnapshot: {
|
|
24587
|
+
storedEntries: args.warmSnapshotStoredEntries
|
|
24588
|
+
},
|
|
24589
|
+
sessionContext: {
|
|
24590
|
+
included: args.includeSessionContext,
|
|
24591
|
+
maxTokens: args.maxContextTokens
|
|
24592
|
+
},
|
|
24593
|
+
workflow: summarizeWorkflowStateForContinuity(workflow2),
|
|
24594
|
+
teamSync: summarizeTeamSyncForContinuity(),
|
|
24595
|
+
source: summarizeSourceSnapshot(sourceSnapshot),
|
|
24596
|
+
sessionSnapshot: sessionSnapshot ? {
|
|
24597
|
+
latestActivityAt: sessionSnapshot.summary.latestActivityAt,
|
|
24598
|
+
latestTitle: sessionSnapshot.summary.latestActivityTitle,
|
|
24599
|
+
latestKind: sessionSnapshot.summary.latestActivityKind,
|
|
24600
|
+
risk: sessionSnapshot.summary.risk,
|
|
24601
|
+
riskReasons: sessionSnapshot.summary.riskReasons,
|
|
24602
|
+
touchedFiles: sessionSnapshot.summary.touchedFiles,
|
|
24603
|
+
nextAction: sessionSnapshot.summary.recommendedNextAction
|
|
24604
|
+
} : null,
|
|
24605
|
+
nextActions: [
|
|
24606
|
+
{
|
|
24607
|
+
id: "open_bootstrap_brief",
|
|
24608
|
+
label: "Open bootstrap brief",
|
|
24609
|
+
command: "snipara-companion session-bootstrap --include-session-context --max-context-tokens 1000",
|
|
24610
|
+
when: "bootstrap has entries or session context is included"
|
|
24611
|
+
},
|
|
24612
|
+
{
|
|
24613
|
+
id: "refresh_source_snapshot",
|
|
24614
|
+
label: "Refresh source snapshot",
|
|
24615
|
+
command: "snipara-companion source sync --json",
|
|
24616
|
+
when: "source.status is missing or local code/docs changed"
|
|
24617
|
+
},
|
|
24618
|
+
{
|
|
24619
|
+
id: "run_impact_gate",
|
|
24620
|
+
label: "Run code impact before risky edits",
|
|
24621
|
+
command: 'snipara-companion code impact --changed-files <files> --diff-summary "next edit"',
|
|
24622
|
+
when: "the next edit is multi-file, risky, or user-visible"
|
|
24623
|
+
},
|
|
24624
|
+
{
|
|
24625
|
+
id: "commit_task_context",
|
|
24626
|
+
label: "Commit durable task context",
|
|
24627
|
+
command: 'snipara-companion task-commit --summary "<done>" --files <files>',
|
|
24628
|
+
when: "a durable phase or task is complete"
|
|
24629
|
+
}
|
|
24630
|
+
]
|
|
24631
|
+
};
|
|
24632
|
+
}
|
|
24633
|
+
async function continueWorkspaceCommand(options) {
|
|
24634
|
+
const configured = isConfigured();
|
|
24635
|
+
const resolvedContextTokens = options.maxContextTokens !== void 0 ? options.maxContextTokens : options.includeSessionContext ? DEFAULT_SESSION_CONTEXT_TOKENS : 0;
|
|
24636
|
+
let bootstrap = emptySessionMemoriesResult();
|
|
24637
|
+
let warmSnapshotStoredEntries = 0;
|
|
24638
|
+
if (configured) {
|
|
24639
|
+
const client = createClient(15e3);
|
|
24640
|
+
bootstrap = await client.getSessionMemories(options.maxCriticalTokens, resolvedContextTokens);
|
|
24641
|
+
const config = loadConfig();
|
|
24642
|
+
const warmSnapshot = createLocalQueryCache({
|
|
24643
|
+
cwd: process.cwd(),
|
|
24644
|
+
projectId: config.projectId,
|
|
24645
|
+
sessionId: config.sessionId
|
|
24646
|
+
}).storeWarmSnapshot(bootstrap);
|
|
24647
|
+
warmSnapshotStoredEntries = warmSnapshot.storedEntries;
|
|
24648
|
+
}
|
|
24649
|
+
const bootstrapQuality = buildSessionBootstrapQuality(bootstrap, {
|
|
24650
|
+
expectedMaxTokens: (options.maxCriticalTokens ?? DEFAULT_FULL_WORKFLOW_CRITICAL_TOKENS) + resolvedContextTokens
|
|
24651
|
+
});
|
|
24652
|
+
const payload = buildCompanionContinuityPayload({
|
|
24653
|
+
configured,
|
|
24654
|
+
bootstrap,
|
|
24655
|
+
bootstrapQuality,
|
|
24656
|
+
includeSessionContext: resolvedContextTokens > 0,
|
|
24657
|
+
maxContextTokens: resolvedContextTokens,
|
|
24658
|
+
warmSnapshotStoredEntries
|
|
24659
|
+
});
|
|
24660
|
+
if (options.json) {
|
|
24661
|
+
printJson2(payload);
|
|
24662
|
+
return;
|
|
24663
|
+
}
|
|
24664
|
+
console.log(import_chalk6.default.bold("Snipara Companion Continuity"));
|
|
24665
|
+
console.log(`Version: ${COMPANION_CONTINUITY_CONTRACT_VERSION}`);
|
|
24666
|
+
console.log(`Configured: ${configured ? "yes" : "no"}`);
|
|
24667
|
+
console.log(`Workflow: ${payload.workflow.status ?? "unknown"}`);
|
|
24668
|
+
console.log(`Source: ${payload.source.status ?? "unknown"}`);
|
|
24669
|
+
console.log("Next: snipara-companion continue-workspace --json for editor integrations");
|
|
24670
|
+
}
|
|
24485
24671
|
async function recallCommand(options) {
|
|
24486
24672
|
ensureConfigured();
|
|
24487
24673
|
const client = createClient(15e3);
|
|
@@ -30389,6 +30575,9 @@ var ADVISOR_RECEIPT_WRITE_LIMIT = 6;
|
|
|
30389
30575
|
function normalizeStringList6(values) {
|
|
30390
30576
|
return [...new Set((values ?? []).map((value) => value.trim()).filter(Boolean))];
|
|
30391
30577
|
}
|
|
30578
|
+
function uniqueStrings18(values) {
|
|
30579
|
+
return [...new Set(values.map((value) => value.trim()).filter(Boolean))];
|
|
30580
|
+
}
|
|
30392
30581
|
function isRecord17(value) {
|
|
30393
30582
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
30394
30583
|
}
|
|
@@ -30646,6 +30835,73 @@ function verificationExecutedFromEvidence(evidence) {
|
|
|
30646
30835
|
(item) => item.command ? `${item.label}: ${item.command} (${item.status})` : `${item.label}: ${item.status}`
|
|
30647
30836
|
);
|
|
30648
30837
|
}
|
|
30838
|
+
function buildPolicyDecisionRequest(args) {
|
|
30839
|
+
const decision = args.run.policyGates.projectPolicyDecision;
|
|
30840
|
+
if (!decision || decision.verdict === "allow") return void 0;
|
|
30841
|
+
const changedFiles = args.run.brief.changedFiles.slice(0, 24);
|
|
30842
|
+
const matchedRules = decision.matchedRules.slice(0, 8);
|
|
30843
|
+
const options = decision.verdict === "block" ? ["respect_block", "request_exception", "mark_policy_stale"] : ["approve_once", "require_changes", "mark_policy_stale", "keep_advisory"];
|
|
30844
|
+
const recommendation = decision.verdict === "block" ? "respect_block" : "approve_once";
|
|
30845
|
+
return buildDecisionRequest({
|
|
30846
|
+
producer: {
|
|
30847
|
+
kind: "project_policy_review",
|
|
30848
|
+
command: "run --emit-policy-decisions",
|
|
30849
|
+
sourceRef: decision.receipt.receiptId
|
|
30850
|
+
},
|
|
30851
|
+
decision: `project_policy_${decision.verdict}`,
|
|
30852
|
+
question: decision.verdict === "block" ? "Project Policy blocked this action. Should the agent stop, request an exception, or mark the policy stale?" : "Project Policy requires human review. May the agent proceed once, change plan, or mark the policy stale?",
|
|
30853
|
+
evidence: {
|
|
30854
|
+
summary: `Project Policy verdict ${decision.verdict} for task '${args.run.brief.task ?? "unspecified"}'. Receipt ${decision.receipt.receiptId}.`,
|
|
30855
|
+
refs: uniqueStrings18([decision.receipt.receiptId, ...decision.receipt.ruleRefs]),
|
|
30856
|
+
items: matchedRules.map((rule) => ({
|
|
30857
|
+
ref: rule.source.ref,
|
|
30858
|
+
title: rule.title,
|
|
30859
|
+
summary: rule.requirement,
|
|
30860
|
+
kind: rule.source.kind,
|
|
30861
|
+
status: rule.strength,
|
|
30862
|
+
metadata: {
|
|
30863
|
+
ruleId: rule.id,
|
|
30864
|
+
scope: rule.scope,
|
|
30865
|
+
confidence: rule.confidence,
|
|
30866
|
+
reviewStatus: rule.source.reviewStatus ?? null
|
|
30867
|
+
}
|
|
30868
|
+
})),
|
|
30869
|
+
reasonCodes: uniqueStrings18(["project_policy_review", ...decision.reasonCodes]),
|
|
30870
|
+
files: changedFiles,
|
|
30871
|
+
applyPath: "manual_context_review",
|
|
30872
|
+
applyCommand: "Resolve with `snipara-companion workflow decide <request-id> --choose <option> --reviewer <name>`; if policy is stale, update or invalidate the cited decision/policy before rerunning `snipara-companion run`."
|
|
30873
|
+
},
|
|
30874
|
+
options,
|
|
30875
|
+
recommendation,
|
|
30876
|
+
rationale: "Project Policy decisions stay agent-first: the agent asks for explicit human governance and records a response receipt; no dashboard or silent policy mutation is required.",
|
|
30877
|
+
blocking: true,
|
|
30878
|
+
fingerprintParts: [
|
|
30879
|
+
"project_policy_review_v1",
|
|
30880
|
+
decision.receipt.receiptId,
|
|
30881
|
+
decision.verdict,
|
|
30882
|
+
decision.receipt.ruleRefs,
|
|
30883
|
+
changedFiles
|
|
30884
|
+
]
|
|
30885
|
+
});
|
|
30886
|
+
}
|
|
30887
|
+
function emitPolicyDecisionRequests(run) {
|
|
30888
|
+
const requests = [buildPolicyDecisionRequest({ run })].filter(
|
|
30889
|
+
(request) => Boolean(request)
|
|
30890
|
+
);
|
|
30891
|
+
const writes = requests.map((request) => writeDecisionRequest(request));
|
|
30892
|
+
return {
|
|
30893
|
+
version: "project-intelligence.policy-decision-requests.v1",
|
|
30894
|
+
emitted: true,
|
|
30895
|
+
requestCount: requests.length,
|
|
30896
|
+
requests,
|
|
30897
|
+
writes,
|
|
30898
|
+
caveats: [
|
|
30899
|
+
"Policy decision requests never apply policy changes automatically.",
|
|
30900
|
+
"Resolve them with workflow decide so the human choice is recorded as a local response receipt.",
|
|
30901
|
+
"Marking a policy stale is a governance signal; the cited memory or policy still needs explicit update or invalidation."
|
|
30902
|
+
]
|
|
30903
|
+
};
|
|
30904
|
+
}
|
|
30649
30905
|
async function recordFirstPartyAdvisorReceipts(args) {
|
|
30650
30906
|
const allRecommendations = args.judgmentCard.advisorRecommendations;
|
|
30651
30907
|
if (args.options.skipAdvisorReceipts) {
|
|
@@ -30937,7 +31193,7 @@ async function buildProjectIntelligenceRun(options) {
|
|
|
30937
31193
|
...!options.skipPackageReview && packageReview?.status !== "ok" ? [packageReviewCommand3()] : []
|
|
30938
31194
|
] : []
|
|
30939
31195
|
];
|
|
30940
|
-
|
|
31196
|
+
const result = {
|
|
30941
31197
|
version: "project-intelligence.production-run.v1",
|
|
30942
31198
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
30943
31199
|
release: Boolean(options.release),
|
|
@@ -30950,6 +31206,10 @@ async function buildProjectIntelligenceRun(options) {
|
|
|
30950
31206
|
judgmentCard,
|
|
30951
31207
|
suggestedCommands: [...new Set(suggestedCommands3)]
|
|
30952
31208
|
};
|
|
31209
|
+
if (options.emitPolicyDecisions) {
|
|
31210
|
+
result.policyDecisionRequests = emitPolicyDecisionRequests(result);
|
|
31211
|
+
}
|
|
31212
|
+
return result;
|
|
30953
31213
|
}
|
|
30954
31214
|
async function projectIntelligenceRunCommand(options) {
|
|
30955
31215
|
const result = await buildProjectIntelligenceRun(options);
|
|
@@ -33118,6 +33378,9 @@ program.command("lead-plan").description("Create a fail-closed Companion Enginee
|
|
|
33118
33378
|
program.command("run").description("Run the production Project Intelligence judgment flow for a task or release").option("--task <task>", "Current task or change summary").option("--branch <branch>", "Branch to scope continuity signals").option("--changed-files <changedFiles...>", "Changed files to analyze").option("--recent-files <recentFiles...>", "Recently touched files for continuity lookup").option("--diff-summary <diffSummary>", "Natural-language summary for code impact").option("--max-tokens <number>", "Resume context token budget", "4000").option("--release", "Run release-oriented guard and package surface review").option("--skip-impact", "Do not run companion code impact").option("--skip-memory-health", "Do not call snipara_memory_health").option("--skip-guard", "Skip collaboration guard during release runs").option("--skip-package-review", "Skip npm package surface review").option("--served-judgment-id <id>", "Served judgment id to use for first-party advisor receipts").option("--skip-advisor-receipts", "Skip first-party advisor influence receipt capture").option(
|
|
33119
33379
|
"--outcome-receipts <files...>",
|
|
33120
33380
|
"Outcome Intelligence V0 receipt JSON files for local calibration"
|
|
33381
|
+
).option(
|
|
33382
|
+
"--emit-policy-decisions",
|
|
33383
|
+
"Write local Decision Requests for Project Policy review/block findings"
|
|
33121
33384
|
).option("--json", "Print raw JSON").action(async (options) => {
|
|
33122
33385
|
await projectIntelligenceRunCommand({
|
|
33123
33386
|
task: options.task,
|
|
@@ -33134,6 +33397,7 @@ program.command("run").description("Run the production Project Intelligence judg
|
|
|
33134
33397
|
servedJudgmentId: options.servedJudgmentId,
|
|
33135
33398
|
skipAdvisorReceipts: Boolean(options.skipAdvisorReceipts),
|
|
33136
33399
|
outcomeReceiptFiles: options.outcomeReceipts,
|
|
33400
|
+
emitPolicyDecisions: Boolean(options.emitPolicyDecisions),
|
|
33137
33401
|
json: Boolean(options.json)
|
|
33138
33402
|
});
|
|
33139
33403
|
});
|
|
@@ -34867,6 +35131,19 @@ program.command("session-bootstrap").description(
|
|
|
34867
35131
|
json: options.json
|
|
34868
35132
|
});
|
|
34869
35133
|
});
|
|
35134
|
+
program.command("continue-workspace").description("Print the stable Companion Continuity Contract for editor integrations").option("--max-critical-tokens <number>", "Durable memory token budget").option(
|
|
35135
|
+
"--include-session-context",
|
|
35136
|
+
"Include short-lived session carryover in addition to durable memory"
|
|
35137
|
+
).option("--max-context-tokens <number>", "Short-lived session context token budget").option("--json", "Print raw JSON").action(async (options) => {
|
|
35138
|
+
const maxCriticalTokens = options.maxCriticalTokens !== void 0 ? parseInt(options.maxCriticalTokens, 10) : void 0;
|
|
35139
|
+
const maxContextTokens = options.maxContextTokens !== void 0 ? parseInt(options.maxContextTokens, 10) : void 0;
|
|
35140
|
+
await continueWorkspaceCommand({
|
|
35141
|
+
maxCriticalTokens,
|
|
35142
|
+
maxContextTokens,
|
|
35143
|
+
includeSessionContext: Boolean(options.includeSessionContext || options.maxContextTokens),
|
|
35144
|
+
json: options.json
|
|
35145
|
+
});
|
|
35146
|
+
});
|
|
34870
35147
|
program.command("task-commit").description("Persist durable outcomes after meaningful task work, not every git commit").requiredOption("-s, --summary <summary>", "Task summary").option("-c, --category <category>", "Category").option("-o, --outcome <outcome>", "Outcome", "completed").option("-f, --files <files...>", "Files touched").option("--json", "Print raw JSON").action(async (options) => {
|
|
34871
35148
|
await taskCommitCommand({
|
|
34872
35149
|
summary: options.summary,
|
package/docs/FULL_REFERENCE.md
CHANGED
|
@@ -21,6 +21,35 @@ snipara-companion code impact --changed-files src/app.ts --diff-summary "next ed
|
|
|
21
21
|
snipara-companion task-commit --summary "completed durable change" --files src/app.ts
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
+
## Companion Continuity Contract V1
|
|
25
|
+
|
|
26
|
+
For editor integrations and "continue this workspace" flows, use the stable JSON
|
|
27
|
+
contract instead of rebuilding Companion internals:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
snipara-companion continue-workspace --include-session-context --json
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
The response version is `snipara.companion.continuity.v1`. It includes:
|
|
34
|
+
|
|
35
|
+
- `project`: configured project id, API URL, and session id when available.
|
|
36
|
+
- `bootstrap` and `bootstrapQuality`: pushed session memories plus token,
|
|
37
|
+
freshness, and warning metadata.
|
|
38
|
+
- `workflow`: active workflow id, goal, current phase, phase statuses, and local
|
|
39
|
+
state path.
|
|
40
|
+
- `teamSync`: active/stale/completed counts and latest handoff summary.
|
|
41
|
+
- `source`: passive `.snipara/source/latest.json` status; no source sync is
|
|
42
|
+
performed by this command.
|
|
43
|
+
- `sessionSnapshot`: latest local activity summary, risks, touched files, and
|
|
44
|
+
recommended next action.
|
|
45
|
+
- `artifacts` and `nextActions`: stable local paths and commands an editor can
|
|
46
|
+
surface to the user.
|
|
47
|
+
|
|
48
|
+
This contract starts after `create-snipara` has activated the workspace. Editor
|
|
49
|
+
extensions should orchestrate these commands and render the payload, not fork
|
|
50
|
+
local source scanning, First Work Brief, memory candidate, or Hosted MCP config
|
|
51
|
+
logic.
|
|
52
|
+
|
|
24
53
|
In this repository, the source currently lives in `packages/cli`, and the installed executable is `snipara-companion`.
|
|
25
54
|
|
|
26
55
|
This package complements `snipara-mcp`. It does not replace it.
|
|
@@ -259,6 +288,13 @@ snipara-companion workflow resume --include-session-context
|
|
|
259
288
|
path declared. Repeated resolved receipts with the same human choice and
|
|
260
289
|
rationale can emit a review-only policy suggestion decision request; the
|
|
261
290
|
suggestion still has a manual apply path and is never auto-applied.
|
|
291
|
+
- `run --emit-policy-decisions` keeps Project Policy administration agent-first:
|
|
292
|
+
when reviewed project policy evidence produces a `require_review` or `block`
|
|
293
|
+
verdict, Companion writes a local `project_policy_review` Decision Request.
|
|
294
|
+
The human resolves it with `workflow decide`; choices such as approving once,
|
|
295
|
+
requiring changes, respecting a block, requesting an exception, or marking
|
|
296
|
+
policy stale are recorded as receipts. No policy is edited or invalidated
|
|
297
|
+
automatically.
|
|
262
298
|
- `memory reviews --scope project --emit-decisions` reads hosted memory review
|
|
263
299
|
queue, clean-candidate, and duplicate-candidate surfaces, summarizes each item,
|
|
264
300
|
and writes local Decision Request artifacts without mutating hosted memory.
|