@gajae-code/coding-agent 0.12.11 → 0.12.13
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/CHANGELOG.md +68 -0
- package/dist/types/cli/marketplace-hint.d.ts +28 -0
- package/dist/types/cli/notify-cli.d.ts +42 -1
- package/dist/types/cli/update-cli.d.ts +9 -3
- package/dist/types/cli.d.ts +10 -0
- package/dist/types/commands/notify.d.ts +6 -0
- package/dist/types/config/file-lock-gc.d.ts +13 -1
- package/dist/types/config/file-lock.d.ts +3 -0
- package/dist/types/config/model-registry.d.ts +6 -0
- package/dist/types/config/models-config-schema.d.ts +99 -0
- package/dist/types/coordinator/contract.d.ts +1 -1
- package/dist/types/coordinator-mcp/question-state.d.ts +3 -1
- package/dist/types/extensibility/extensions/index.d.ts +1 -0
- package/dist/types/extensibility/extensions/ouroboros-ooo-bridge.d.ts +13 -0
- package/dist/types/extensibility/extensions/prefix-command-bridge.d.ts +0 -1
- package/dist/types/extensibility/extensions/types.d.ts +2 -0
- package/dist/types/gjc-runtime/deep-interview-state.d.ts +14 -0
- package/dist/types/gjc-runtime/gc-runtime.d.ts +11 -0
- package/dist/types/main.d.ts +6 -2
- package/dist/types/modes/acp/acp-agent.d.ts +24 -1
- package/dist/types/modes/components/session-selector.d.ts +1 -1
- package/dist/types/modes/utils/host-status.d.ts +23 -0
- package/dist/types/runtime-mcp/types.d.ts +2 -0
- package/dist/types/sdk/broker/lifecycle.d.ts +12 -0
- package/dist/types/sdk/bus/chat-daemon-cli.d.ts +3 -0
- package/dist/types/sdk/bus/chat-daemon-command-channel.d.ts +244 -0
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +11 -1
- package/dist/types/sdk/bus/chat-daemon-runtime.d.ts +7 -0
- package/dist/types/sdk/bus/control-plane-frames.d.ts +43 -0
- package/dist/types/sdk/bus/conversation-store.d.ts +18 -0
- package/dist/types/sdk/bus/existing-thread-readiness.d.ts +71 -0
- package/dist/types/sdk/bus/html-format.d.ts +10 -0
- package/dist/types/sdk/bus/notification-service.d.ts +10 -0
- package/dist/types/sdk/bus/slack-daemon.d.ts +27 -0
- package/dist/types/sdk/bus/slack-live-provider.d.ts +11 -0
- package/dist/types/sdk/bus/slack-provider.d.ts +14 -0
- package/dist/types/sdk/bus/slack-thread-binding.d.ts +140 -0
- package/dist/types/sdk/bus/telegram-daemon-cli.d.ts +2 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +28 -7
- package/dist/types/sdk/bus/telegram-daemon.d.ts +119 -17
- package/dist/types/sdk/bus/telegram-reference.d.ts +20 -1
- package/dist/types/sdk/bus/topic-registry.d.ts +130 -85
- package/dist/types/sdk/host/host.d.ts +52 -0
- package/dist/types/sdk/lifecycle-session.d.ts +6 -0
- package/dist/types/sdk/session-activation.d.ts +41 -0
- package/dist/types/sdk/session.d.ts +18 -0
- package/dist/types/sdk/startup-capability.d.ts +8 -1
- package/dist/types/session/agent-session.d.ts +7 -2
- package/dist/types/session/artifacts.d.ts +2 -2
- package/dist/types/session/session-manager.d.ts +40 -6
- package/dist/types/tools/index.d.ts +15 -0
- package/dist/types/utils/npm-registry.d.ts +71 -0
- package/examples/extensions/README.md +38 -4
- package/examples/extensions/ooo-bridge.ts +15 -0
- package/package.json +8 -8
- package/scripts/generate-sdk-operation-inventory.ts +4 -0
- package/src/async/job-manager.ts +9 -2
- package/src/cli/fast-help.ts +1 -0
- package/src/cli/marketplace-hint.ts +39 -0
- package/src/cli/notify-cli.ts +226 -2
- package/src/cli/plugin-cli.ts +10 -0
- package/src/cli/update-cli.ts +90 -23
- package/src/cli.ts +23 -0
- package/src/commands/notify.ts +27 -3
- package/src/commands/sdk.ts +1 -0
- package/src/config/file-lock-gc.ts +65 -26
- package/src/config/file-lock.ts +61 -16
- package/src/config/model-profiles.ts +46 -30
- package/src/config/model-registry.ts +17 -5
- package/src/config/models-config-schema.ts +9 -4
- package/src/coordinator/contract.ts +1 -0
- package/src/coordinator-mcp/question-state.ts +2 -0
- package/src/coordinator-mcp/server.ts +358 -7
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +1 -1
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +2 -0
- package/src/extensibility/extensions/index.ts +1 -0
- package/src/extensibility/extensions/ouroboros-ooo-bridge.ts +235 -0
- package/src/extensibility/extensions/prefix-command-bridge.ts +4 -9
- package/src/extensibility/extensions/runner.ts +15 -2
- package/src/extensibility/extensions/types.ts +2 -0
- package/src/gjc-runtime/deep-interview-recorder.ts +9 -5
- package/src/gjc-runtime/deep-interview-state.ts +27 -3
- package/src/gjc-runtime/gc-render.ts +8 -1
- package/src/gjc-runtime/gc-runtime.ts +15 -1
- package/src/gjc-runtime/launch-tmux.ts +53 -1
- package/src/internal-urls/docs-index.generated.ts +14 -13
- package/src/main.ts +214 -70
- package/src/modes/DESIGN.md +8 -5
- package/src/modes/acp/acp-agent.ts +123 -7
- package/src/modes/components/footer.ts +10 -18
- package/src/modes/components/model-selector.ts +52 -5
- package/src/modes/components/session-selector.ts +26 -3
- package/src/modes/components/status-line/gh.ts +23 -2
- package/src/modes/controllers/event-controller.ts +3 -0
- package/src/modes/controllers/extension-ui-controller.ts +2 -0
- package/src/modes/controllers/input-controller.ts +11 -0
- package/src/modes/controllers/selector-controller.ts +8 -2
- package/src/modes/interactive-mode.ts +7 -1
- package/src/modes/print-mode.ts +10 -0
- package/src/modes/utils/host-status.ts +38 -0
- package/src/prompts/system/project-prompt.md +19 -0
- package/src/runtime-mcp/types.ts +2 -0
- package/src/sdk/broker/lifecycle.ts +60 -3
- package/src/sdk/bus/chat-daemon-cli.ts +42 -0
- package/src/sdk/bus/chat-daemon-command-channel.ts +921 -0
- package/src/sdk/bus/chat-daemon-control.ts +16 -3
- package/src/sdk/bus/chat-daemon-runtime.ts +257 -21
- package/src/sdk/bus/control-plane-frames.ts +51 -0
- package/src/sdk/bus/conversation-store.ts +29 -1
- package/src/sdk/bus/existing-thread-readiness.ts +155 -0
- package/src/sdk/bus/html-format.ts +19 -3
- package/src/sdk/bus/index.ts +91 -12
- package/src/sdk/bus/notification-service.ts +28 -1
- package/src/sdk/bus/rich-render.ts +3 -2
- package/src/sdk/bus/slack-daemon.ts +99 -4
- package/src/sdk/bus/slack-live-provider.ts +32 -2
- package/src/sdk/bus/slack-provider.ts +11 -0
- package/src/sdk/bus/slack-thread-binding.ts +641 -0
- package/src/sdk/bus/telegram-daemon-cli.ts +11 -0
- package/src/sdk/bus/telegram-daemon-contract.ts +28 -7
- package/src/sdk/bus/telegram-daemon.ts +2007 -509
- package/src/sdk/bus/telegram-reference.ts +114 -15
- package/src/sdk/bus/topic-registry.ts +674 -181
- package/src/sdk/host/host.ts +180 -1
- package/src/sdk/lifecycle-session.ts +8 -2
- package/src/sdk/protocol/operation-inventory.generated.json +22 -0
- package/src/sdk/session-activation.ts +127 -0
- package/src/sdk/session.ts +144 -29
- package/src/sdk/startup-capability.ts +10 -1
- package/src/session/agent-session.ts +220 -13
- package/src/session/artifacts.ts +56 -13
- package/src/session/internal/managed-session-storage.ts +228 -7
- package/src/session/session-manager.ts +225 -60
- package/src/setup/provider-presets.json +3 -3
- package/src/task/index.ts +92 -44
- package/src/thinking-metadata.ts +2 -1
- package/src/tools/ask.ts +10 -0
- package/src/tools/index.ts +15 -0
- package/src/tools/todo-write.ts +4 -4
- package/src/utils/npm-registry.ts +660 -0
|
@@ -346,10 +346,20 @@ export interface ResumeTailTerminal {
|
|
|
346
346
|
}
|
|
347
347
|
export interface ResumeTailError {
|
|
348
348
|
kind: "error";
|
|
349
|
-
reason: "missing" | "malformed" | "unstable" | "read-failed" | "legacy_migration_disabled";
|
|
349
|
+
reason: "missing" | "malformed" | "unstable" | "read-failed" | "legacy_migration_disabled" | "oversized";
|
|
350
|
+
size?: number;
|
|
350
351
|
}
|
|
351
352
|
export type SessionDirectoryMigrationPolicy = ManagedMigrationPolicy;
|
|
352
353
|
export type SessionAppendPersistenceFailurePhase = "current_append" | "prior_failure";
|
|
354
|
+
/**
|
|
355
|
+
* Safety bound for resuming a single transcript file. Matches the managed-storage
|
|
356
|
+
* per-file artifact limit (`MANAGED_ARTIFACT_MAX_FILE_BYTES`): transcripts at or above
|
|
357
|
+
* this size are not loaded into memory wholesale because the decode + per-line JSON
|
|
358
|
+
* parse + identity hash would OOM or stall the process (#3851). The bound is a
|
|
359
|
+
* load-time guard only; it does not raise the on-disk managed-storage limit.
|
|
360
|
+
*/
|
|
361
|
+
export declare const RESUME_TRANSCRIPT_MAX_BYTES: number;
|
|
362
|
+
export declare const SESSION_OVERSIZED_RECOVERY_MESSAGE = "The selected session transcript is too large to resume safely. Use `gjc export <session-file>` to export its content into a new session, or remove/archive it after confirming its content is no longer needed.";
|
|
353
363
|
export declare class SessionAppendPersistenceError extends Error {
|
|
354
364
|
readonly phase: SessionAppendPersistenceFailurePhase;
|
|
355
365
|
readonly entryId: string;
|
|
@@ -368,6 +378,11 @@ export declare class SessionArtifactCapacityError extends Error {
|
|
|
368
378
|
readonly code = "artifact_capacity_exceeded";
|
|
369
379
|
constructor(message: string);
|
|
370
380
|
}
|
|
381
|
+
export declare class SessionTranscriptOversizedError extends Error {
|
|
382
|
+
readonly code = "oversized";
|
|
383
|
+
readonly size: number;
|
|
384
|
+
constructor(size: number);
|
|
385
|
+
}
|
|
371
386
|
export type ResumeTailInspection = ResumeTailResumable | ResumeTailTerminal | ResumeTailError;
|
|
372
387
|
export interface StrictSessionOpenSuccess {
|
|
373
388
|
kind: "opened";
|
|
@@ -377,6 +392,7 @@ export interface StrictSessionOpenFailure {
|
|
|
377
392
|
kind: "error";
|
|
378
393
|
reason: ResumeTailError["reason"] | "identity-mismatch" | "migration-required" | "artifact_capacity_exceeded" | "migration_busy";
|
|
379
394
|
message?: string;
|
|
395
|
+
size?: number;
|
|
380
396
|
}
|
|
381
397
|
/** Exact transcript bytes and authority captured from one descriptor-bound read. */
|
|
382
398
|
export interface CapturedSessionTranscriptSnapshot {
|
|
@@ -615,6 +631,7 @@ interface SessionManagerStateSnapshot {
|
|
|
615
631
|
needsFullRewriteOnNextPersist: boolean;
|
|
616
632
|
fileEntries: FileEntry[];
|
|
617
633
|
materializedFileEntries: FileEntry[];
|
|
634
|
+
adoptedArtifactManager: ArtifactManager | null;
|
|
618
635
|
}
|
|
619
636
|
/** Benchmark-derived cap for strong materialized session snapshots. */
|
|
620
637
|
export declare const MATERIALIZED_CACHE_MAX_BYTES: number;
|
|
@@ -623,6 +640,7 @@ export declare const SessionManagerTestHooks: {
|
|
|
623
640
|
materializedCacheMaxBytesOverride?: number;
|
|
624
641
|
beforeResidentTransitionIndexBuild?: () => void;
|
|
625
642
|
afterForkSnapshot?: () => void | Promise<void>;
|
|
643
|
+
beforeEphemeralArtifactManagerInstall?: (dir: string) => void | Promise<void>;
|
|
626
644
|
};
|
|
627
645
|
export declare class SessionManager {
|
|
628
646
|
#private;
|
|
@@ -643,7 +661,9 @@ export declare class SessionManager {
|
|
|
643
661
|
captureState(): SessionManagerStateSnapshot;
|
|
644
662
|
restoreState(snapshot: SessionManagerStateSnapshot): void;
|
|
645
663
|
/** Switch to a different session file (used for resume and branching). */
|
|
646
|
-
setSessionFile(sessionFile: string
|
|
664
|
+
setSessionFile(sessionFile: string, options?: {
|
|
665
|
+
deferEphemeralArtifactRetirement?: boolean;
|
|
666
|
+
}): Promise<void>;
|
|
647
667
|
/** Start a new session. Closes any existing writer first. */
|
|
648
668
|
newSession(options?: NewSessionOptions): Promise<string | undefined>;
|
|
649
669
|
/**
|
|
@@ -751,13 +771,23 @@ export declare class SessionManager {
|
|
|
751
771
|
* the parent session's artifact directory and ID counter.
|
|
752
772
|
*/
|
|
753
773
|
adoptArtifactManager(manager: ArtifactManager): void;
|
|
774
|
+
/** Release only the matching externally adopted manager. */
|
|
775
|
+
releaseArtifactManager(manager: ArtifactManager): void;
|
|
776
|
+
/** Temporarily release adopted authority while an outer transition validates its successor. */
|
|
777
|
+
stageAdoptedArtifactManagerForTransition(): void;
|
|
778
|
+
/** Prove manager authority by exact object identity, never by pathname shape. */
|
|
779
|
+
isArtifactManagerAuthorized(manager: ArtifactManager): boolean;
|
|
754
780
|
/**
|
|
755
781
|
* Returns the ArtifactManager this session writes through. Lazily creates
|
|
756
782
|
* one bound to the current session file unless an external manager was
|
|
757
|
-
* adopted via `adoptArtifactManager`.
|
|
758
|
-
*
|
|
783
|
+
* adopted via `adoptArtifactManager`. Falls back to the lazily created
|
|
784
|
+
* ephemeral filesystem store once a non-persistent session has saved an
|
|
785
|
+
* artifact, so `artifact://` stays resolvable. Returns null only when no
|
|
786
|
+
* store has been established yet.
|
|
759
787
|
*/
|
|
760
788
|
getArtifactManager(): ArtifactManager | null;
|
|
789
|
+
/** Linearizably establish this session's persistent or ephemeral artifact manager. */
|
|
790
|
+
ensureArtifactManager(): Promise<ArtifactManager | null>;
|
|
761
791
|
/**
|
|
762
792
|
* Allocate a new artifact path and ID for the current session.
|
|
763
793
|
* Returns an empty object when the session is not persisted.
|
|
@@ -768,14 +798,18 @@ export declare class SessionManager {
|
|
|
768
798
|
}>;
|
|
769
799
|
/**
|
|
770
800
|
* Save artifact content under the current session and return artifact ID.
|
|
771
|
-
*
|
|
801
|
+
* Persistent sessions write into the session artifact directory; non-persistent
|
|
802
|
+
* sessions write into a lazily created temporary directory so the content is
|
|
803
|
+
* read back from the filesystem instead of being retained in memory.
|
|
772
804
|
*/
|
|
773
805
|
saveArtifact(content: string, toolType: string): Promise<string | undefined>;
|
|
774
806
|
/**
|
|
775
807
|
* Resolve an artifact ID to an on-disk path for the current session.
|
|
776
|
-
* Returns null when
|
|
808
|
+
* Returns null when the artifact is missing.
|
|
777
809
|
*/
|
|
778
810
|
getArtifactPath(id: string): Promise<string | null>;
|
|
811
|
+
/** Retire predecessor ephemeral artifacts after an outer logical transition commits. */
|
|
812
|
+
retireEphemeralArtifactsAfterTransition(): void;
|
|
779
813
|
/**
|
|
780
814
|
* Persist (or clear) the current editor draft so the next resume of this
|
|
781
815
|
* session can restore it. Empty text deletes any stale draft. No-op when the
|
|
@@ -84,6 +84,13 @@ export interface AskAnswerRequest {
|
|
|
84
84
|
controls: readonly AskRemoteControl[];
|
|
85
85
|
/** Optional zero-based recommendation into the authoritative raw options. */
|
|
86
86
|
recommendedIndex?: number;
|
|
87
|
+
/** True while the question accepts several options before the navigation control commits. */
|
|
88
|
+
multi?: boolean;
|
|
89
|
+
/**
|
|
90
|
+
* Options currently selected in the multi-select loop, as authoritative option
|
|
91
|
+
* labels. Remote transports render the selection state so a toggle is visible.
|
|
92
|
+
*/
|
|
93
|
+
selectedOptions?: readonly string[];
|
|
87
94
|
}
|
|
88
95
|
export type AskRemoteInteraction = {
|
|
89
96
|
kind: "value";
|
|
@@ -219,6 +226,14 @@ export interface ToolSession {
|
|
|
219
226
|
getAuthorizedArtifactsDirs?: () => readonly string[];
|
|
220
227
|
/** Get the ArtifactManager backing this session (shared across parent + subagents). */
|
|
221
228
|
getArtifactManager?: () => ArtifactManager | null;
|
|
229
|
+
/** Prove that an ArtifactManager belongs to this concrete session or its explicitly adopted parent tree. */
|
|
230
|
+
isArtifactManagerAuthorized?: (manager: ArtifactManager) => boolean;
|
|
231
|
+
/** Adopt a task-created fallback manager into the concrete session owner. */
|
|
232
|
+
adoptArtifactManager?: (manager: ArtifactManager) => void;
|
|
233
|
+
/** Linearizably establish the concrete session owner's canonical artifact manager. */
|
|
234
|
+
ensureArtifactManager?: () => Promise<ArtifactManager | null>;
|
|
235
|
+
/** Release a task-created fallback manager when its logical session ends. */
|
|
236
|
+
releaseArtifactManager?: (manager: ArtifactManager) => void;
|
|
222
237
|
/** Register teardown work owned by the current logical session. */
|
|
223
238
|
registerSessionCleanup?: (cleanup: () => Promise<void> | void) => () => void;
|
|
224
239
|
/** Allocate a new artifact path and ID for session-scoped truncated output. */
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
export declare const DEFAULT_NPM_REGISTRY = "https://registry.npmjs.org";
|
|
2
|
+
export type NpmConfigOrigin = "env" | "user" | "global";
|
|
3
|
+
/** Which package manager will run the install this check is gating. */
|
|
4
|
+
export type Installer = "bun" | "npm";
|
|
5
|
+
/** Injection points so tests never read the real environment. */
|
|
6
|
+
export interface NpmRegistryEnvironment {
|
|
7
|
+
/** Manager that will perform the install; decides Bun-vs-npm config priority. */
|
|
8
|
+
installer?: Installer;
|
|
9
|
+
/** Resolves an executable on PATH; used to locate npm's effective prefix. */
|
|
10
|
+
whichExecutable?: (name: string) => string | undefined;
|
|
11
|
+
/** Defaults to `$credentialEnv`, which excludes the caller's `cwd/.env`. */
|
|
12
|
+
lookupEnv?: (name: string) => string | undefined;
|
|
13
|
+
homeDir?: string;
|
|
14
|
+
platform?: NodeJS.Platform;
|
|
15
|
+
readFile?: (filePath: string) => Promise<string | undefined>;
|
|
16
|
+
}
|
|
17
|
+
export interface ResolvedNpmRegistry {
|
|
18
|
+
registry: string;
|
|
19
|
+
headers: Record<string, string>;
|
|
20
|
+
origin: NpmConfigOrigin | "default";
|
|
21
|
+
/** Where the value came from, e.g. `$npm_config_registry` or `~/.npmrc`. */
|
|
22
|
+
source: string;
|
|
23
|
+
/** Config files that exist but could not be read. */
|
|
24
|
+
warnings: string[];
|
|
25
|
+
}
|
|
26
|
+
export interface LatestPackageVersion {
|
|
27
|
+
version: string;
|
|
28
|
+
registry: string;
|
|
29
|
+
/** Config problems that did not stop the lookup but changed its outcome. */
|
|
30
|
+
warnings: string[];
|
|
31
|
+
}
|
|
32
|
+
export interface FetchResponseLike {
|
|
33
|
+
ok: boolean;
|
|
34
|
+
status: number;
|
|
35
|
+
statusText: string;
|
|
36
|
+
json: () => Promise<unknown>;
|
|
37
|
+
}
|
|
38
|
+
export type FetchLike = (input: string, init?: {
|
|
39
|
+
headers?: Record<string, string>;
|
|
40
|
+
signal?: AbortSignal;
|
|
41
|
+
}) => Promise<FetchResponseLike>;
|
|
42
|
+
export interface NpmRegistryLookupOptions extends NpmRegistryEnvironment {
|
|
43
|
+
fetchImpl?: FetchLike;
|
|
44
|
+
timeoutMs?: number;
|
|
45
|
+
}
|
|
46
|
+
/** Thrown when a registry is configured but unusable, so it is never silently ignored. */
|
|
47
|
+
export declare class NpmRegistryConfigError extends Error {
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Parse an `.npmrc` into raw key/value pairs. Later duplicates win, as in npm.
|
|
51
|
+
*
|
|
52
|
+
* @internal Exported for tests; production code goes through `resolveNpmRegistry`.
|
|
53
|
+
*/
|
|
54
|
+
export declare function parseNpmrc(contents: string, lookupEnv?: (name: string) => string | undefined): Map<string, string>;
|
|
55
|
+
/**
|
|
56
|
+
* Build the credential lookup keys npm calls "nerf darts": `//host/path/`,
|
|
57
|
+
* walking up one path segment at a time until the bare host.
|
|
58
|
+
*
|
|
59
|
+
* @internal Exported for tests; production code goes through `resolveNpmRegistry`.
|
|
60
|
+
*/
|
|
61
|
+
export declare function credentialPrefixes(registry: string): string[];
|
|
62
|
+
/** Resolve the registry npm/bun would use for `packageName`, with credentials attached. */
|
|
63
|
+
export declare function resolveNpmRegistry(packageName: string, environment?: NpmRegistryEnvironment): Promise<ResolvedNpmRegistry>;
|
|
64
|
+
/**
|
|
65
|
+
* Registries can live under a path prefix, so join by string rather than `new URL`.
|
|
66
|
+
*
|
|
67
|
+
* @internal Exported for tests; production code goes through `fetchLatestPackageVersion`.
|
|
68
|
+
*/
|
|
69
|
+
export declare function buildRegistryPackageUrl(registry: string, packageName: string, spec?: string): string;
|
|
70
|
+
/** Fetch the latest published version of `packageName` from the configured registry. */
|
|
71
|
+
export declare function fetchLatestPackageVersion(packageName: string, options?: NpmRegistryLookupOptions): Promise<LatestPackageVersion>;
|
|
@@ -14,6 +14,39 @@ mkdir -p .gjc/extensions
|
|
|
14
14
|
cp packages/coding-agent/examples/extensions/pirate.ts .gjc/extensions/
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
+
### Enable the Ouroboros `ooo` bridge
|
|
18
|
+
|
|
19
|
+
Install the version-pinned Ouroboros `v0.50.7` MCP profile, then configure its GJC runtime:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
uv tool install 'ouroboros-ai[mcp]==0.50.7'
|
|
23
|
+
ouroboros setup --runtime gjc
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
`pipx install 'ouroboros-ai[mcp]==0.50.7'` is the equivalent pipx installation. Do not pipe a mutable branch installer into a shell. Pin source audits to commit `cb658aa819bfabafecbbe91bc36327f10691171b`. The [v0.50.7 release](https://github.com/Q00/ouroboros/releases/tag/v0.50.7) publishes `ouroboros_ai-0.50.7-py3-none-any.whl` with SHA-256 `df42f4ef10e032f2edc3249534bf91e8612dee789dfc3517895a9eb2df7f82c4`; verify downloaded release assets before installing them.
|
|
27
|
+
|
|
28
|
+
Ouroboros setup installs its own managed bridge. Replace that file with this standalone GJC bridge, which preserves the interview session across serialized follow-up answers, disposes it on GJC session switches, and drops queued predecessor-session starts. Download the example from immutable GJC commit `4311fefd49e9c6781c4d1111b8dd3f758e7d8974` and verify it before installation:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
curl -fL https://raw.githubusercontent.com/Yeachan-Heo/gajae-code/4311fefd49e9c6781c4d1111b8dd3f758e7d8974/packages/coding-agent/examples/extensions/ooo-bridge.ts -o /tmp/gjc-ooo-bridge.ts
|
|
32
|
+
shasum -a 256 /tmp/gjc-ooo-bridge.ts
|
|
33
|
+
mkdir -p "${HOME}/${GJC_CONFIG_DIR:-.gjc}/agent/extensions/ouroboros-ooo-bridge" && cp /tmp/gjc-ooo-bridge.ts "${HOME}/${GJC_CONFIG_DIR:-.gjc}/agent/extensions/ouroboros-ooo-bridge/index.ts"
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
The `shasum` output must match `2b0e1e25ac145331f112da629076875542db6f6e63c3c17adcd6770a4dcaf7bd` before the copy. The file has no runtime package imports and uses the host API injected by GJC, so compiled binaries do not require a peer `node_modules` directory beside the installation.
|
|
37
|
+
|
|
38
|
+
For a project-only installation, copy the same verified file to `.gjc/extensions/ouroboros-ooo-bridge/index.ts`. Start a new GJC session after installation, then enter:
|
|
39
|
+
|
|
40
|
+
```text
|
|
41
|
+
ooo interview "I want to build a task management CLI"
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
The first question is rendered in GJC. While that interview remains active, ordinary interactive input is sent as the answer with the same Ouroboros session ID; completion clears the correlation and returns subsequent ordinary prompts to GJC. Other `ooo ...` commands continue through `ouroboros dispatch --runtime gjc`, including exit-code `78` pass-through.
|
|
45
|
+
|
|
46
|
+
Set `OUROBOROS_CLI=/absolute/path/to/ouroboros` when the executable is outside `PATH`. Missing executable, MCP startup, and dispatch failures produce an error notification for the claimed input without preventing GJC startup or ordinary prompts.
|
|
47
|
+
|
|
48
|
+
This external path is separate from GJC's native `/skill:deep-interview`: the native skill runs GJC's bundled interview workflow, while `ooo interview` delegates to the installed Ouroboros MCP interview tool.
|
|
49
|
+
|
|
17
50
|
## Examples
|
|
18
51
|
|
|
19
52
|
### Custom Tools & API
|
|
@@ -39,10 +72,11 @@ cp packages/coding-agent/examples/extensions/pirate.ts .gjc/extensions/
|
|
|
39
72
|
|
|
40
73
|
### External Dependencies
|
|
41
74
|
|
|
42
|
-
| Extension | Description
|
|
43
|
-
| ----------------- |
|
|
44
|
-
| `chalk-logger.ts` | Uses chalk from parent node_modules (demonstrates jiti module resolution)
|
|
45
|
-
| `
|
|
75
|
+
| Extension | Description |
|
|
76
|
+
| ----------------- | ---------------------------------------------------------------------------- |
|
|
77
|
+
| `chalk-logger.ts` | Uses chalk from parent node_modules (demonstrates jiti module resolution) |
|
|
78
|
+
| `ooo-bridge.ts` | Opt-in `ooo ...` input bridge to the installed Ouroboros CLI and MCP runtime |
|
|
79
|
+
| `with-deps/` | Extension with its own package.json and dependencies |
|
|
46
80
|
|
|
47
81
|
## Writing Extensions
|
|
48
82
|
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
interface OooBridgeExtensionAPI {
|
|
2
|
+
pi: unknown;
|
|
3
|
+
on(event: "input" | "session_switch", handler: (event: unknown, context: unknown) => unknown): void;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
interface OooBridgeHost {
|
|
7
|
+
createOuroborosOooBridge(): ((event: unknown, context: unknown) => unknown) & { reset(): Promise<void> };
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export default function (pi: OooBridgeExtensionAPI) {
|
|
11
|
+
const host = pi.pi as OooBridgeHost;
|
|
12
|
+
const bridge = host.createOuroborosOooBridge();
|
|
13
|
+
pi.on("input", bridge);
|
|
14
|
+
pi.on("session_switch", () => bridge.reset());
|
|
15
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@gajae-code/coding-agent",
|
|
4
|
-
"version": "0.12.
|
|
4
|
+
"version": "0.12.13",
|
|
5
5
|
"description": "Gajae Code CLI with read, bash, edit, write tools and session management",
|
|
6
6
|
"homepage": "https://gajae-code.com",
|
|
7
7
|
"author": "Yeachan-Heo and Gajae Code Contributors",
|
|
@@ -53,13 +53,13 @@
|
|
|
53
53
|
"@agentclientprotocol/sdk": "1.3.0",
|
|
54
54
|
"@babel/parser": "^7.29.3",
|
|
55
55
|
"@mozilla/readability": "^0.6.0",
|
|
56
|
-
"@gajae-code/stats": "0.12.
|
|
57
|
-
"@gajae-code/agent-core": "0.12.
|
|
58
|
-
"@gajae-code/ai": "0.12.
|
|
59
|
-
"@gajae-code/bridge-client": "0.12.
|
|
60
|
-
"@gajae-code/natives": "0.12.
|
|
61
|
-
"@gajae-code/tui": "0.12.
|
|
62
|
-
"@gajae-code/utils": "0.12.
|
|
56
|
+
"@gajae-code/stats": "0.12.13",
|
|
57
|
+
"@gajae-code/agent-core": "0.12.13",
|
|
58
|
+
"@gajae-code/ai": "0.12.13",
|
|
59
|
+
"@gajae-code/bridge-client": "0.12.13",
|
|
60
|
+
"@gajae-code/natives": "0.12.13",
|
|
61
|
+
"@gajae-code/tui": "0.12.13",
|
|
62
|
+
"@gajae-code/utils": "0.12.13",
|
|
63
63
|
"@puppeteer/browsers": "^2.13.0",
|
|
64
64
|
"@types/turndown": "5.0.6",
|
|
65
65
|
"@xterm/headless": "^6.0.0",
|
|
@@ -37,6 +37,8 @@ const LOCKED_EXCLUSIONS: Readonly<Record<string, string>> = {
|
|
|
37
37
|
"agent_session:constructor": "internal accessor/plumbing, not a user-facing control seam",
|
|
38
38
|
"agent_session:registerToolSessionCleanup":
|
|
39
39
|
"internal tool lifecycle cleanup registration, not a user-facing SDK control seam",
|
|
40
|
+
"agent_session:registerToolSessionTransitionCleanup":
|
|
41
|
+
"internal tool transition cleanup registration for shared artifact-manager ownership, not a user-facing SDK control seam",
|
|
40
42
|
"agent_session:nextToolChoice": "internal accessor/plumbing, not a user-facing control seam",
|
|
41
43
|
"agent_session:setForcedToolChoice": "internal accessor/plumbing, not a user-facing control seam",
|
|
42
44
|
"agent_session:getActiveSkillState": "internal accessor/plumbing, not a user-facing control seam",
|
|
@@ -66,6 +68,8 @@ const LOCKED_EXCLUSIONS: Readonly<Record<string, string>> = {
|
|
|
66
68
|
"agent_session:closeWriterStrict": "internal ACP lifecycle teardown plumbing, not a user-facing control seam",
|
|
67
69
|
"agent_session:disposeChildSubprocesses": "internal accessor/plumbing, not a user-facing control seam",
|
|
68
70
|
"agent_session:waitForIdle": "internal accessor/plumbing, not a user-facing control seam",
|
|
71
|
+
"agent_session:extendStartupTurnBarrier":
|
|
72
|
+
"internal CLI startup-readiness fence composition, not a user-facing SDK control seam",
|
|
69
73
|
"agent_session:awaitPendingContextTransformations":
|
|
70
74
|
"internal context-transformation lifecycle barrier, not a user-facing SDK control seam",
|
|
71
75
|
"agent_session:drainAsyncJobDeliveriesForAcp": "internal accessor/plumbing, not a user-facing control seam",
|
package/src/async/job-manager.ts
CHANGED
|
@@ -463,6 +463,7 @@ export class AsyncJobManager {
|
|
|
463
463
|
readonly #terminalWaits = new Map<string, TerminalWaitState>();
|
|
464
464
|
#waitSeq = 0;
|
|
465
465
|
readonly #publishedTerminalGenerations = new Set<string>();
|
|
466
|
+
readonly #settledJobIds = new Set<string>();
|
|
466
467
|
#jobGenerationSeq = 0;
|
|
467
468
|
readonly #liveHandles = new Map<string, SubagentLiveHandle>();
|
|
468
469
|
readonly #subagentProgress = new Map<string, AgentProgress>();
|
|
@@ -820,6 +821,7 @@ export class AsyncJobManager {
|
|
|
820
821
|
|
|
821
822
|
this.#expireMonitorTombstones();
|
|
822
823
|
const id = this.#resolveJobId(options?.id);
|
|
824
|
+
this.#settledJobIds.delete(id);
|
|
823
825
|
const abortController = new AbortController();
|
|
824
826
|
const startTime = Date.now();
|
|
825
827
|
|
|
@@ -919,7 +921,9 @@ export class AsyncJobManager {
|
|
|
919
921
|
this.#scheduleEviction(id);
|
|
920
922
|
this.#drainResumeQueue();
|
|
921
923
|
}
|
|
922
|
-
})()
|
|
924
|
+
})().finally(() => {
|
|
925
|
+
this.#settledJobIds.add(id);
|
|
926
|
+
});
|
|
923
927
|
|
|
924
928
|
this.#jobs.set(id, job);
|
|
925
929
|
this.#notifyChange();
|
|
@@ -1218,6 +1222,8 @@ export class AsyncJobManager {
|
|
|
1218
1222
|
) {
|
|
1219
1223
|
continue;
|
|
1220
1224
|
}
|
|
1225
|
+
if (job.status === "cancelled" && this.#settledJobIds.has(job.id) && !this.#subagentRecords.has(subagentId))
|
|
1226
|
+
continue;
|
|
1221
1227
|
if (!targets.has(subagentId)) {
|
|
1222
1228
|
targets.set(subagentId, { subagentId, jobId: job.id, source: "metadata_job" });
|
|
1223
1229
|
}
|
|
@@ -1298,7 +1304,7 @@ export class AsyncJobManager {
|
|
|
1298
1304
|
lease.targets.map(target => target.subagentId),
|
|
1299
1305
|
);
|
|
1300
1306
|
}
|
|
1301
|
-
if (state.phase === "proved" && state.proof) return state.proof;
|
|
1307
|
+
if (state.phase === "proved" && state.proof?.confirmed) return state.proof;
|
|
1302
1308
|
state.phase = "proving";
|
|
1303
1309
|
const settled = new Set<string>();
|
|
1304
1310
|
const promises: Promise<void>[] = [];
|
|
@@ -2064,6 +2070,7 @@ export class AsyncJobManager {
|
|
|
2064
2070
|
this.#purgeTerminalSubagentStateForJob(jobId);
|
|
2065
2071
|
const job = this.#jobs.get(jobId);
|
|
2066
2072
|
this.#jobs.delete(jobId);
|
|
2073
|
+
this.#settledJobIds.delete(jobId);
|
|
2067
2074
|
this.#lifecycles.delete(jobId);
|
|
2068
2075
|
this.#lifecyclePhases.delete(jobId);
|
|
2069
2076
|
this.#deadLetteredDeliveries.delete(jobId);
|
package/src/cli/fast-help.ts
CHANGED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Install-failure hints for marketplace-shaped plugin names.
|
|
3
|
+
*
|
|
4
|
+
* A bare spec (no `@marketplace`) is always classified as npm, so a user who
|
|
5
|
+
* copies a plugin name out of `plugin discover` gets an npm resolution failure
|
|
6
|
+
* with no hint that the same name is offered by a registered marketplace.
|
|
7
|
+
*
|
|
8
|
+
* Kept dependency-free — the marketplace barrel loads native addons — so this
|
|
9
|
+
* takes the narrow catalog surface it actually reads instead of the manager.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/** The catalog lookups this module needs; `MarketplaceManager` satisfies it. */
|
|
13
|
+
export interface MarketplaceCatalogLookup {
|
|
14
|
+
listMarketplaces(): Promise<ReadonlyArray<{ name: string }>>;
|
|
15
|
+
getPluginInfo(name: string, marketplace: string): Promise<unknown | null>;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* A bare name carries no `@scope`, version specifier, or path separator, so it
|
|
20
|
+
* is the only install spec shape that is safe to echo back in an error.
|
|
21
|
+
*/
|
|
22
|
+
export function isBareInstallName(spec: string): boolean {
|
|
23
|
+
return spec.length > 0 && !spec.includes("@") && !spec.includes("/") && !spec.includes("\\");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Names of registered marketplaces whose catalog offers a plugin under exactly
|
|
28
|
+
* `name`. Only marketplace names are returned: they are user-chosen registry
|
|
29
|
+
* labels, never the plugin source, so they carry no credentials or home paths.
|
|
30
|
+
*/
|
|
31
|
+
export async function findMarketplacesOffering(catalogs: MarketplaceCatalogLookup, name: string): Promise<string[]> {
|
|
32
|
+
const marketplaces = await catalogs.listMarketplaces();
|
|
33
|
+
const offering: string[] = [];
|
|
34
|
+
for (const marketplace of marketplaces) {
|
|
35
|
+
const info = await catalogs.getPluginInfo(name, marketplace.name).catch(() => null);
|
|
36
|
+
if (info) offering.push(marketplace.name);
|
|
37
|
+
}
|
|
38
|
+
return offering;
|
|
39
|
+
}
|