@cjhyy/code-shell-pet 0.8.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.
Files changed (74) hide show
  1. package/README.md +99 -0
  2. package/dist/capability.d.ts +15 -0
  3. package/dist/capability.js +251 -0
  4. package/dist/current-time.d.ts +5 -0
  5. package/dist/current-time.js +43 -0
  6. package/dist/delegate-work.d.ts +7 -0
  7. package/dist/delegate-work.js +142 -0
  8. package/dist/delegation.d.ts +33 -0
  9. package/dist/delegation.js +6 -0
  10. package/dist/disclosure/catalog.d.ts +22 -0
  11. package/dist/disclosure/catalog.js +104 -0
  12. package/dist/disclosure/constants.d.ts +7 -0
  13. package/dist/disclosure/constants.js +7 -0
  14. package/dist/disclosure/jsonl.d.ts +9 -0
  15. package/dist/disclosure/jsonl.js +61 -0
  16. package/dist/disclosure/latest-result.d.ts +8 -0
  17. package/dist/disclosure/latest-result.js +35 -0
  18. package/dist/disclosure/search.d.ts +21 -0
  19. package/dist/disclosure/search.js +124 -0
  20. package/dist/disclosure/selector.d.ts +2 -0
  21. package/dist/disclosure/selector.js +5 -0
  22. package/dist/disclosure/todo-snapshot.d.ts +8 -0
  23. package/dist/disclosure/todo-snapshot.js +54 -0
  24. package/dist/follow-ups.d.ts +21 -0
  25. package/dist/follow-ups.js +102 -0
  26. package/dist/gateway.d.ts +41 -0
  27. package/dist/gateway.js +263 -0
  28. package/dist/host-actions.d.ts +51 -0
  29. package/dist/host-actions.js +405 -0
  30. package/dist/index.capability.d.ts +7 -0
  31. package/dist/index.capability.js +7 -0
  32. package/dist/index.d.ts +32 -0
  33. package/dist/index.disclosure.d.ts +13 -0
  34. package/dist/index.disclosure.js +13 -0
  35. package/dist/index.js +32 -0
  36. package/dist/index.protocol.d.ts +8 -0
  37. package/dist/index.protocol.js +8 -0
  38. package/dist/index.team.d.ts +2 -0
  39. package/dist/index.team.js +2 -0
  40. package/dist/long-task.d.ts +251 -0
  41. package/dist/long-task.js +795 -0
  42. package/dist/mobile-remote.d.ts +14 -0
  43. package/dist/mobile-remote.js +39 -0
  44. package/dist/outbound-message.d.ts +15 -0
  45. package/dist/outbound-message.js +127 -0
  46. package/dist/pending-decision-index.d.ts +24 -0
  47. package/dist/pending-decision-index.js +110 -0
  48. package/dist/profile.d.ts +27 -0
  49. package/dist/profile.js +120 -0
  50. package/dist/projection-extension.d.ts +8 -0
  51. package/dist/projection-extension.js +238 -0
  52. package/dist/protocol.d.ts +58 -0
  53. package/dist/protocol.js +15 -0
  54. package/dist/report-to-mimi.d.ts +7 -0
  55. package/dist/report-to-mimi.js +87 -0
  56. package/dist/run-params.d.ts +34 -0
  57. package/dist/run-params.js +387 -0
  58. package/dist/segment-closure.d.ts +52 -0
  59. package/dist/segment-closure.js +130 -0
  60. package/dist/session-control.d.ts +5 -0
  61. package/dist/session-control.js +46 -0
  62. package/dist/session-index.d.ts +22 -0
  63. package/dist/session-index.js +324 -0
  64. package/dist/sessions-tool.d.ts +21 -0
  65. package/dist/sessions-tool.js +145 -0
  66. package/dist/team.d.ts +13 -0
  67. package/dist/team.js +38 -0
  68. package/dist/tool-arguments.d.ts +1 -0
  69. package/dist/tool-arguments.js +10 -0
  70. package/dist/topic-segment.d.ts +55 -0
  71. package/dist/topic-segment.js +37 -0
  72. package/dist/types.d.ts +95 -0
  73. package/dist/types.js +1 -0
  74. package/package.json +56 -0
@@ -0,0 +1,238 @@
1
+ import { GET_PET_PROJECTION_SNAPSHOT_METHOD, PET_PROJECTION_DELTA_METHOD, } from "./protocol.js";
2
+ import { PendingDecisionIndex } from "./pending-decision-index.js";
3
+ import { SessionIndex } from "./session-index.js";
4
+ import { LOCAL_PET_OWNER, } from "./types.js";
5
+ /** Session kinds owned by the pet domain — hidden from generic session lists. */
6
+ export const PET_HIDDEN_SESSION_KINDS = ["pet"];
7
+ /**
8
+ * Build the pet projection observer. All former AgentServer pet state and
9
+ * methods live here, keyed off the domain-neutral host surface.
10
+ */
11
+ export function createPetProjectionObserver(host) {
12
+ const pendingDecisionIndex = new PendingDecisionIndex();
13
+ const petSessionIndex = new SessionIndex();
14
+ const petCatalog = new Map();
15
+ let petProjectionVersion = 0;
16
+ let petProjectionDisconnected = false;
17
+ let petProjectionClosing = false;
18
+ const petWorkerGeneration = () => host.projectionGeneration();
19
+ const nextPetProjectionVersion = () => {
20
+ petProjectionVersion += 1;
21
+ return petProjectionVersion;
22
+ };
23
+ const sendPetProjectionDelta = (delta) => {
24
+ // Socket teardown can resolve a fail-closed approval and finish its run.
25
+ // Do not write projection tail events to a transport whose owner is gone.
26
+ if (host.isTransportDisconnected() && !petProjectionClosing)
27
+ return;
28
+ host.notify(PET_PROJECTION_DELTA_METHOD, delta);
29
+ };
30
+ const ensurePetSession = (sessionId, updatedAt = Date.now()) => {
31
+ const previous = petCatalog.get(sessionId);
32
+ petCatalog.set(sessionId, {
33
+ sessionId,
34
+ updatedAt: Math.max(previous?.updatedAt ?? 0, updatedAt),
35
+ });
36
+ petSessionIndex.replaceCatalog({
37
+ owner: LOCAL_PET_OWNER,
38
+ sessions: [...petCatalog.values()],
39
+ observedAt: updatedAt,
40
+ });
41
+ };
42
+ const currentPetSessionProjection = (sessionId, observedAt) => {
43
+ const live = host.getLiveSessionSnapshot().find((session) => session.sessionId === sessionId);
44
+ if (!live || live.kind === "pet")
45
+ return undefined;
46
+ ensurePetSession(sessionId, live.lastActivityAt);
47
+ const indexed = petSessionIndex.get(sessionId);
48
+ const pendingDecisionCount = pendingDecisionIndex
49
+ .pendingSnapshot()
50
+ .filter((entry) => entry.agentSessionId === sessionId).length;
51
+ const liveRunState = live.busy ? "running" : live.queueDepth > 0 ? "queued" : "idle";
52
+ const terminal = liveRunState === "idle" ? indexed?.terminal : undefined;
53
+ const runState = terminal ? "terminal" : liveRunState;
54
+ return {
55
+ owner: LOCAL_PET_OWNER,
56
+ agentSessionId: sessionId,
57
+ coreSessionId: sessionId,
58
+ title: indexed?.title,
59
+ workspaceDisplayName: indexed?.workspaceDisplayName,
60
+ runState,
61
+ phase: pendingDecisionCount > 0 ? "waiting-decision" : indexed?.phase,
62
+ summary: pendingDecisionCount > 0
63
+ ? pendingDecisionCount === 1
64
+ ? "等待用户决定"
65
+ : `等待用户决定(${pendingDecisionCount})`
66
+ : (indexed?.summary ?? (runState === "running" ? "运行中" : undefined)),
67
+ queueDepth: live.queueDepth,
68
+ lastActivityAt: Math.max(live.lastActivityAt, indexed?.lastActivityAt ?? 0),
69
+ pendingDecisionCount,
70
+ completionKind: liveRunState === "idle" ? indexed?.completionKind : undefined,
71
+ terminal,
72
+ freshness: { source: "live-snapshot", observedAt, workerState: "active" },
73
+ };
74
+ };
75
+ const buildPetProjectionSnapshot = () => {
76
+ const observedAt = Date.now();
77
+ const sessions = host
78
+ .getLiveSessionSnapshot()
79
+ .filter((session) => session.kind !== "pet")
80
+ .map((session) => currentPetSessionProjection(session.sessionId, observedAt))
81
+ .filter((session) => session !== undefined)
82
+ .sort((a, b) => a.agentSessionId.localeCompare(b.agentSessionId));
83
+ return {
84
+ snapshotVersion: petProjectionVersion,
85
+ workerGeneration: petWorkerGeneration(),
86
+ observedAt,
87
+ sessions,
88
+ pending: pendingDecisionIndex.pendingSnapshot(),
89
+ };
90
+ };
91
+ const recordPetStreamEvent = (sessionId, event) => {
92
+ const observedAt = Date.now();
93
+ const live = host.getLiveSessionSnapshot().find((session) => session.sessionId === sessionId);
94
+ if (live?.kind === "pet")
95
+ return;
96
+ ensurePetSession(sessionId, observedAt);
97
+ const version = nextPetProjectionVersion();
98
+ petSessionIndex.applyStreamEvent({
99
+ sessionId,
100
+ event,
101
+ generation: petWorkerGeneration(),
102
+ version,
103
+ observedAt,
104
+ });
105
+ const session = currentPetSessionProjection(sessionId, observedAt);
106
+ if (!session)
107
+ return;
108
+ sendPetProjectionDelta({
109
+ workerGeneration: petWorkerGeneration(),
110
+ version,
111
+ observedAt,
112
+ kind: "session-upsert",
113
+ session,
114
+ });
115
+ };
116
+ const emitPetSessionUpsert = (sessionId) => {
117
+ const observedAt = Date.now();
118
+ const session = currentPetSessionProjection(sessionId, observedAt);
119
+ if (!session)
120
+ return;
121
+ sendPetProjectionDelta({
122
+ workerGeneration: petWorkerGeneration(),
123
+ version: nextPetProjectionVersion(),
124
+ observedAt,
125
+ kind: "session-upsert",
126
+ session,
127
+ });
128
+ };
129
+ const emitPetSessionRemove = (sessionId) => {
130
+ const observedAt = Date.now();
131
+ sendPetProjectionDelta({
132
+ workerGeneration: petWorkerGeneration(),
133
+ version: nextPetProjectionVersion(),
134
+ observedAt,
135
+ kind: "session-remove",
136
+ sessionId,
137
+ });
138
+ };
139
+ const emitPetPendingUpsert = (pending) => {
140
+ const observedAt = Date.now();
141
+ sendPetProjectionDelta({
142
+ workerGeneration: petWorkerGeneration(),
143
+ version: nextPetProjectionVersion(),
144
+ observedAt,
145
+ kind: "pending-upsert",
146
+ pending,
147
+ });
148
+ };
149
+ const transitionPendingDecision = (metadata, status) => {
150
+ const observedAt = Date.now();
151
+ if (!pendingDecisionIndex.transition({
152
+ sessionId: metadata.sessionId,
153
+ requestId: metadata.requestId,
154
+ routeGeneration: metadata.routeGeneration,
155
+ status,
156
+ terminalAt: observedAt,
157
+ })) {
158
+ return;
159
+ }
160
+ sendPetProjectionDelta({
161
+ workerGeneration: petWorkerGeneration(),
162
+ version: nextPetProjectionVersion(),
163
+ observedAt,
164
+ kind: "pending-remove",
165
+ sessionId: metadata.sessionId,
166
+ requestId: metadata.requestId,
167
+ status,
168
+ });
169
+ emitPetSessionUpsert(metadata.sessionId);
170
+ };
171
+ const emitPetWorkerDisconnected = () => {
172
+ if (petProjectionDisconnected)
173
+ return;
174
+ petProjectionDisconnected = true;
175
+ const observedAt = Date.now();
176
+ const version = nextPetProjectionVersion();
177
+ petSessionIndex.applyWorkerLifecycle({
178
+ state: "disconnected",
179
+ generation: petWorkerGeneration(),
180
+ version,
181
+ observedAt,
182
+ });
183
+ sendPetProjectionDelta({
184
+ workerGeneration: petWorkerGeneration(),
185
+ version,
186
+ observedAt,
187
+ kind: "worker-state",
188
+ state: "disconnected",
189
+ });
190
+ };
191
+ // Compat channel: the wire method name stays agent/getPetProjectionSnapshot;
192
+ // the server routes it through this registration instead of pet-specific code.
193
+ host.registerQuery(GET_PET_PROJECTION_SNAPSHOT_METHOD, () => buildPetProjectionSnapshot());
194
+ return {
195
+ onSessionAttached: (sessionId, lastActivityAt) => {
196
+ ensurePetSession(sessionId, lastActivityAt);
197
+ },
198
+ onSessionStream: (sessionId, event) => {
199
+ recordPetStreamEvent(sessionId, event);
200
+ },
201
+ onRunBoundary: (sessionId) => {
202
+ emitPetSessionUpsert(sessionId);
203
+ },
204
+ onApprovalCreated: (metadata) => {
205
+ if (host.getSessionKind(metadata.sessionId) === "pet") {
206
+ metadata = { ...metadata, surfaceable: false };
207
+ }
208
+ if (pendingDecisionIndex.created(metadata)) {
209
+ const pending = pendingDecisionIndex.get(metadata.sessionId, metadata.requestId);
210
+ if (pending) {
211
+ emitPetPendingUpsert(pending);
212
+ emitPetSessionUpsert(metadata.sessionId);
213
+ }
214
+ }
215
+ return metadata;
216
+ },
217
+ onApprovalTransition: (metadata, status) => {
218
+ if (status === "pending")
219
+ return;
220
+ transitionPendingDecision(metadata, status);
221
+ },
222
+ onSessionClosed: (sessionId) => {
223
+ const observedAt = Date.now();
224
+ petCatalog.delete(sessionId);
225
+ petSessionIndex.replaceCatalog({
226
+ owner: LOCAL_PET_OWNER,
227
+ sessions: [...petCatalog.values()],
228
+ observedAt,
229
+ });
230
+ emitPetSessionRemove(sessionId);
231
+ },
232
+ onServerClose: () => {
233
+ petProjectionClosing = true;
234
+ emitPetWorkerDisconnected();
235
+ },
236
+ snapshotPendingDecisions: () => pendingDecisionIndex.snapshot(),
237
+ };
238
+ }
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Wire shapes for the Pet projection RPC surface. Deltas are pushed by the
3
+ * worker-side projection extension over the legacy notification method name
4
+ * `agent/petProjectionDelta`; the snapshot answers
5
+ * `agent/getPetProjectionSnapshot`. (Method names stay stable for the desktop
6
+ * bridge, which intercepts them by name.)
7
+ */
8
+ import type { PendingDecisionProjection, PetProjectionSnapshot, PetSessionProjection, PetWorkerState } from "./types.js";
9
+ export type PetProjectionDelta = {
10
+ workerGeneration: number;
11
+ version: number;
12
+ observedAt: number;
13
+ kind: "session-upsert";
14
+ session: PetSessionProjection;
15
+ } | {
16
+ workerGeneration: number;
17
+ version: number;
18
+ observedAt: number;
19
+ kind: "session-remove";
20
+ sessionId: string;
21
+ } | {
22
+ workerGeneration: number;
23
+ version: number;
24
+ observedAt: number;
25
+ kind: "pending-upsert";
26
+ pending: PendingDecisionProjection;
27
+ } | {
28
+ workerGeneration: number;
29
+ version: number;
30
+ observedAt: number;
31
+ kind: "pending-remove";
32
+ sessionId: string;
33
+ requestId: string;
34
+ status: Exclude<PendingDecisionProjection["status"], "pending">;
35
+ } | {
36
+ workerGeneration: number;
37
+ version: number;
38
+ observedAt: number;
39
+ kind: "worker-state";
40
+ state: PetWorkerState;
41
+ };
42
+ export type PetProjectionSnapshotResult = PetProjectionSnapshot;
43
+ /** Any Session → host report routed to Mimi without revealing her hidden Session id. */
44
+ export interface PetReportToMimiEvent {
45
+ reportId: string;
46
+ sessionId: string;
47
+ message: string;
48
+ attachmentPaths?: string[];
49
+ createdAt: number;
50
+ }
51
+ /**
52
+ * Wire method names, kept byte-identical to the pre-extraction core protocol
53
+ * so the desktop bridge (which intercepts by name) needs no changes. Core's
54
+ * Methods table retains the same strings as a compat surface.
55
+ */
56
+ export declare const PET_PROJECTION_DELTA_METHOD = "agent/petProjectionDelta";
57
+ export declare const GET_PET_PROJECTION_SNAPSHOT_METHOD = "agent/getPetProjectionSnapshot";
58
+ export declare const PET_REPORT_TO_MIMI_METHOD = "agent/petReportToMimi";
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Wire shapes for the Pet projection RPC surface. Deltas are pushed by the
3
+ * worker-side projection extension over the legacy notification method name
4
+ * `agent/petProjectionDelta`; the snapshot answers
5
+ * `agent/getPetProjectionSnapshot`. (Method names stay stable for the desktop
6
+ * bridge, which intercepts them by name.)
7
+ */
8
+ /**
9
+ * Wire method names, kept byte-identical to the pre-extraction core protocol
10
+ * so the desktop bridge (which intercepts by name) needs no changes. Core's
11
+ * Methods table retains the same strings as a compat surface.
12
+ */
13
+ export const PET_PROJECTION_DELTA_METHOD = "agent/petProjectionDelta";
14
+ export const GET_PET_PROJECTION_SNAPSHOT_METHOD = "agent/getPetProjectionSnapshot";
15
+ export const PET_REPORT_TO_MIMI_METHOD = "agent/petReportToMimi";
@@ -0,0 +1,7 @@
1
+ import type { ToolContext, ToolDefinition, ToolVisibilityContext } from "@cjhyy/code-shell-core/extension";
2
+ import type { PetReportToMimiEvent } from "./protocol.js";
3
+ export declare const REPORT_TO_MIMI_TOOL_NAME = "ReportToMimi";
4
+ export declare const reportToMimiToolDef: ToolDefinition;
5
+ export type PetReportToMimiSink = (event: PetReportToMimiEvent) => void;
6
+ export declare function reportToMimiAvailability(ctx: ToolVisibilityContext): boolean;
7
+ export declare function reportToMimiTool(args: Record<string, unknown>, ctx?: ToolContext, report?: PetReportToMimiSink): Promise<string>;
@@ -0,0 +1,87 @@
1
+ import { createHash } from "node:crypto";
2
+ import { isAbsolute } from "node:path";
3
+ import { hasOnlyDeclaredToolArguments } from "./tool-arguments.js";
4
+ export const REPORT_TO_MIMI_TOOL_NAME = "ReportToMimi";
5
+ export const reportToMimiToolDef = {
6
+ name: REPORT_TO_MIMI_TOOL_NAME,
7
+ description: "Report a result, status update, artifact, or question from the current Session to Mimi. " +
8
+ "This works from any Session; Mimi decides how to present or act on the report. " +
9
+ "Do not search for Mimi's hidden Session id and do not provide a channel or recipient.",
10
+ inputSchema: {
11
+ type: "object",
12
+ additionalProperties: false,
13
+ properties: {
14
+ message: {
15
+ type: "string",
16
+ minLength: 1,
17
+ maxLength: 8_000,
18
+ description: "Complete concise report for Mimi, including outcome, evidence, and any user decision needed.",
19
+ },
20
+ attachment_paths: {
21
+ type: "array",
22
+ minItems: 1,
23
+ maxItems: 4,
24
+ uniqueItems: true,
25
+ items: {
26
+ type: "string",
27
+ minLength: 1,
28
+ maxLength: 4_096,
29
+ },
30
+ description: "Optional absolute local artifact paths for Mimi. The host independently decides whether any route may send them.",
31
+ },
32
+ },
33
+ required: ["message"],
34
+ },
35
+ };
36
+ export function reportToMimiAvailability(ctx) {
37
+ return isReportableSessionId(ctx.sessionId);
38
+ }
39
+ export async function reportToMimiTool(args, ctx, report) {
40
+ if (!isReportableSessionId(ctx?.sessionId)) {
41
+ return "Error: ReportToMimi requires a valid current Session.";
42
+ }
43
+ if (!report)
44
+ return "Error: the Mimi host reporting channel is unavailable.";
45
+ if (!hasOnlyDeclaredToolArguments(args, ["message", "attachment_paths"])) {
46
+ return "Error: ReportToMimi accepts only message and attachment_paths.";
47
+ }
48
+ const message = typeof args.message === "string" ? args.message.trim() : "";
49
+ if (!message || message.length > 8_000) {
50
+ return "Error: ReportToMimi message must be 1 to 8000 characters.";
51
+ }
52
+ const attachmentPaths = parseAttachmentPaths(args.attachment_paths);
53
+ if (attachmentPaths === null) {
54
+ return "Error: attachment_paths must contain 1 to 4 unique absolute paths.";
55
+ }
56
+ const reportId = createHash("sha256")
57
+ .update([ctx.sessionId, ctx.originClientMessageId ?? "", message, ...(attachmentPaths ?? [])].join("\0"))
58
+ .digest("hex")
59
+ .slice(0, 32);
60
+ report({
61
+ reportId,
62
+ sessionId: ctx.sessionId,
63
+ message,
64
+ ...(attachmentPaths ? { attachmentPaths } : {}),
65
+ createdAt: Date.now(),
66
+ });
67
+ return "Report accepted by Mimi's host. Delivery is asynchronous; do not search for Mimi's Session id or claim that an IM message was delivered.";
68
+ }
69
+ function parseAttachmentPaths(value) {
70
+ if (value === undefined)
71
+ return undefined;
72
+ if (!Array.isArray(value) ||
73
+ value.length < 1 ||
74
+ value.length > 4 ||
75
+ !value.every((path) => typeof path === "string" && path.length > 0 && path.length <= 4_096 && isAbsolute(path)) ||
76
+ new Set(value).size !== value.length) {
77
+ return null;
78
+ }
79
+ return [...value];
80
+ }
81
+ function isReportableSessionId(value) {
82
+ return (typeof value === "string" &&
83
+ value.length >= 1 &&
84
+ value.length <= 256 &&
85
+ value === value.trim() &&
86
+ !/[\u0000-\u001f\u007f]/u.test(value));
87
+ }
@@ -0,0 +1,34 @@
1
+ import { type PetReusableSessionOption, type PetWorkspaceOption } from "./delegation.js";
2
+ import { type PetGatewayReplyCapability, type PetHostActionKind } from "./host-actions.js";
3
+ import { type PetGatewayCatalog } from "./gateway.js";
4
+ import type { PetFollowUpItem } from "./follow-ups.js";
5
+ import type { PetOutboundTargetOption } from "./outbound-message.js";
6
+ export interface PetRunOptions {
7
+ workspaces: readonly PetWorkspaceOption[];
8
+ reusableSessions: readonly PetReusableSessionOption[];
9
+ /** Host-action kinds the host declared it can execute this turn. */
10
+ hostActionKinds: readonly PetHostActionKind[];
11
+ /** Exact outbound route contract backing GatewayReply for this turn. */
12
+ gatewayReply?: PetGatewayReplyCapability;
13
+ /** Adapter-owned first-level discovery catalog backing the Gateway tool. */
14
+ gateway?: PetGatewayCatalog;
15
+ /** Host-provided sessions directory backing the Sessions tool. */
16
+ sessionsRootDir?: string;
17
+ /** The actionable rows shown in the desktop Needs follow-up section. */
18
+ followUps: readonly PetFollowUpItem[];
19
+ /** Host-authorized proactive owner destinations. */
20
+ outboundTargets: readonly PetOutboundTargetOption[];
21
+ }
22
+ /**
23
+ * Build an immutable, bounded per-turn snapshot for the Pet behavior profile.
24
+ *
25
+ * AgentServer validates the same shapes before dispatch. This second line of
26
+ * defense matters for in-process Engine hosts, which can invoke the behavior
27
+ * profile directly without crossing the protocol validator.
28
+ */
29
+ export declare function petRunOptionsFrom(profileParams: Readonly<Record<string, unknown>>): PetRunOptions;
30
+ /**
31
+ * Pet-specific agent/run params validation. Canonical profileParams keys win
32
+ * over legacy pet aliases, matching Engine's merge precedence.
33
+ */
34
+ export declare function validatePetRunParams(params: Record<string, unknown>): string | null;