@gajae-code/coding-agent 0.18.1 → 0.18.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.
- package/CHANGELOG.md +60 -0
- package/dist/types/cli/stats-cli.d.ts +0 -5
- package/dist/types/config/autorouting-tier-map.d.ts +29 -2
- package/dist/types/config/model-registry.d.ts +16 -1
- package/dist/types/exa/mcp-client.d.ts +1 -5
- package/dist/types/exec/bash-shell-guardian.d.ts +26 -0
- package/dist/types/hashline/parser.d.ts +28 -5
- package/dist/types/modes/acp/acp-agent.d.ts +6 -1
- package/dist/types/sdk/broker/lifecycle.d.ts +6 -1
- package/dist/types/sdk/broker/recovery-backoff.d.ts +62 -0
- package/dist/types/sdk/broker/runtime.d.ts +20 -0
- package/dist/types/sdk/broker/startup-budget.d.ts +5 -1
- package/dist/types/sdk/bus/index.d.ts +1 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +1 -1
- package/dist/types/sdk/host/session-runtime.d.ts +7 -0
- package/dist/types/sdk/prompt-watchdog.d.ts +1 -1
- package/dist/types/tools/bash.d.ts +1 -0
- package/package.json +7 -7
- package/src/cli/stats-cli.ts +4 -36
- package/src/cli-main.ts +4 -8
- package/src/cli.ts +4 -8
- package/src/commands/commit.ts +2 -2
- package/src/commands/sdk.ts +1 -2
- package/src/config/autorouting-tier-map.ts +20 -2
- package/src/config/model-preset-registry.ts +17 -2
- package/src/config/model-profile-activation.ts +3 -1
- package/src/config/model-profiles.ts +11 -3
- package/src/config/model-registry.ts +45 -17
- package/src/exa/mcp-client.ts +41 -8
- package/src/exec/bash-shell-guardian.ts +184 -34
- package/src/hashline/execute.ts +51 -3
- package/src/hashline/parser.ts +63 -16
- package/src/internal-urls/docs-index.generated.ts +22 -22
- package/src/modes/acp/acp-agent.ts +39 -4
- package/src/modes/controllers/ssh-command-controller.ts +3 -1
- package/src/sdk/broker/lifecycle.ts +408 -218
- package/src/sdk/broker/recovery-backoff.ts +135 -0
- package/src/sdk/broker/runtime.ts +83 -0
- package/src/sdk/broker/startup-budget.ts +6 -1
- package/src/sdk/bus/chat-daemon-control.ts +2 -2
- package/src/sdk/bus/index.ts +305 -19
- package/src/sdk/bus/telegram-daemon-contract.ts +1 -1
- package/src/sdk/host/session-runtime.ts +99 -3
- package/src/sdk/prompt-watchdog.ts +17 -4
- package/src/session/agent-session.ts +14 -2
- package/src/session/internal/managed-session-storage.ts +23 -0
- package/src/tools/bash.ts +39 -0
- package/dist/types/cli/grep-cli.d.ts +0 -17
- package/dist/types/cli/shell-cli.d.ts +0 -8
- package/dist/types/commands/grep.d.ts +0 -42
- package/dist/types/commands/shell.d.ts +0 -21
- package/src/cli/grep-cli.ts +0 -160
- package/src/cli/shell-cli.ts +0 -176
- package/src/commands/grep.ts +0 -48
- package/src/commands/shell.ts +0 -29
|
@@ -50,6 +50,12 @@ import { parseThinkingLevel } from "../../thinking";
|
|
|
50
50
|
import { readEndpointFile } from "../broker/endpoint-authority";
|
|
51
51
|
import { ensureBroker } from "../broker/ensure";
|
|
52
52
|
import { processIncarnation } from "../broker/process-incarnation";
|
|
53
|
+
import { RecoveryBackoffTracker } from "../broker/recovery-backoff";
|
|
54
|
+
import {
|
|
55
|
+
captureRuntimeImageIdentity,
|
|
56
|
+
isSdkInternalRuntimeImageReplaced,
|
|
57
|
+
sdkInternalRuntimeImage,
|
|
58
|
+
} from "../broker/runtime";
|
|
53
59
|
import {
|
|
54
60
|
type MasterRoleAttestationV2,
|
|
55
61
|
resolveSessionLocator,
|
|
@@ -716,6 +722,15 @@ export interface CreateSdkSessionRuntimeOptions {
|
|
|
716
722
|
onFailureDiagnosticKeyCountForTests?: (count: number) => void;
|
|
717
723
|
/** Test-only observation after a resolved invocation completion is reconciled. */
|
|
718
724
|
onInvocationCompletionReconciledForTests?: (kind: InvocationKind, correlation: InvocationCorrelation) => void;
|
|
725
|
+
/** Test hook: provides a function to inject a controlled clock for broker recovery backoff assertions. */
|
|
726
|
+
setRecoveryBackoffClockForTest?: (inject: (clock: { now(): number }) => void) => void;
|
|
727
|
+
/** Test hook: inject mock runtime image identity and replacement detection for broker recovery tests. */
|
|
728
|
+
setRuntimeImageIdentityForTest?: (
|
|
729
|
+
register: (
|
|
730
|
+
captureFn: () => Awaited<ReturnType<typeof captureRuntimeImageIdentity>> | undefined,
|
|
731
|
+
isReplacedFn: () => Promise<boolean>,
|
|
732
|
+
) => void,
|
|
733
|
+
) => void;
|
|
719
734
|
}
|
|
720
735
|
|
|
721
736
|
function unavailable(operation: string): () => never {
|
|
@@ -7013,6 +7028,43 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
7013
7028
|
let brokerRegistered = false;
|
|
7014
7029
|
let brokerRegistrationInFlight: Promise<void> | undefined;
|
|
7015
7030
|
let brokerRecoveryStopped = false;
|
|
7031
|
+
let brokerRecoveryRestartRequired = false;
|
|
7032
|
+
const brokerRecoveryBackoff = new RecoveryBackoffTracker();
|
|
7033
|
+
if (options.setRecoveryBackoffClockForTest) {
|
|
7034
|
+
options.setRecoveryBackoffClockForTest((clock: { now(): number }) =>
|
|
7035
|
+
brokerRecoveryBackoff.setClockForTest(clock),
|
|
7036
|
+
);
|
|
7037
|
+
}
|
|
7038
|
+
// Capture runtime image identity at startup to detect replacements
|
|
7039
|
+
let startupRuntimeImageIdentity: Awaited<ReturnType<typeof captureRuntimeImageIdentity>> | undefined;
|
|
7040
|
+
let captureStartupRuntimeImageForTest:
|
|
7041
|
+
| (() => Awaited<ReturnType<typeof captureRuntimeImageIdentity>> | undefined)
|
|
7042
|
+
| undefined;
|
|
7043
|
+
let isRuntimeImageReplacedForTest: (() => Promise<boolean>) | undefined;
|
|
7044
|
+
if (options.setRuntimeImageIdentityForTest) {
|
|
7045
|
+
options.setRuntimeImageIdentityForTest(
|
|
7046
|
+
(
|
|
7047
|
+
captureFn: () => Awaited<ReturnType<typeof captureRuntimeImageIdentity>> | undefined,
|
|
7048
|
+
isReplacedFn: () => Promise<boolean>,
|
|
7049
|
+
) => {
|
|
7050
|
+
captureStartupRuntimeImageForTest = captureFn;
|
|
7051
|
+
isRuntimeImageReplacedForTest = isReplacedFn;
|
|
7052
|
+
},
|
|
7053
|
+
);
|
|
7054
|
+
}
|
|
7055
|
+
const captureStartupRuntimeImage = async (): Promise<void> => {
|
|
7056
|
+
if (options.setRuntimeImageIdentityForTest) {
|
|
7057
|
+
// Test mode: use injected identity
|
|
7058
|
+
startupRuntimeImageIdentity = captureStartupRuntimeImageForTest?.();
|
|
7059
|
+
} else {
|
|
7060
|
+
// Production mode: capture real runtime image
|
|
7061
|
+
const runtimeImage = sdkInternalRuntimeImage();
|
|
7062
|
+
if (runtimeImage) {
|
|
7063
|
+
startupRuntimeImageIdentity = await captureRuntimeImageIdentity(runtimeImage);
|
|
7064
|
+
}
|
|
7065
|
+
}
|
|
7066
|
+
};
|
|
7067
|
+
const startupImageCapture = captureStartupRuntimeImage();
|
|
7016
7068
|
const registerBroker = async (): Promise<void> => {
|
|
7017
7069
|
if (brokerRegistered) return;
|
|
7018
7070
|
if (brokerRegistrationInFlight !== undefined) {
|
|
@@ -7143,17 +7195,58 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
7143
7195
|
let brokerRecoveryTimer: NodeJS.Timeout | undefined;
|
|
7144
7196
|
let brokerRecoveryInFlight: Promise<void> | undefined;
|
|
7145
7197
|
const runBrokerRecovery = async (): Promise<void> => {
|
|
7146
|
-
if (brokerRecoveryStopped || brokerRecoveryInFlight !== undefined) return;
|
|
7198
|
+
if (brokerRecoveryStopped || brokerRecoveryInFlight !== undefined || brokerRecoveryRestartRequired) return;
|
|
7199
|
+
// Checked synchronously so an interval tick cannot start an attempt the backoff forbids.
|
|
7200
|
+
if (!brokerRecoveryBackoff.canAttemptRecovery(options.agentDir)) return;
|
|
7201
|
+
|
|
7202
|
+
// The single-flight slot is claimed before the first await: the interval does not wait
|
|
7203
|
+
// for prior callbacks, so a slow runtime-image probe must not let a second tick through
|
|
7204
|
+
// to ensure the broker and record the same failure twice.
|
|
7147
7205
|
const recovery = (async (): Promise<void> => {
|
|
7206
|
+
// A replaced runtime image cannot launch a broker; restarting the session is the only fix.
|
|
7207
|
+
const imageReplaced = await (isRuntimeImageReplacedForTest
|
|
7208
|
+
? isRuntimeImageReplacedForTest()
|
|
7209
|
+
: isSdkInternalRuntimeImageReplaced(startupRuntimeImageIdentity).catch(() => false));
|
|
7210
|
+
if (imageReplaced) {
|
|
7211
|
+
brokerRecoveryRestartRequired = true;
|
|
7212
|
+
stopBrokerRecovery();
|
|
7213
|
+
logger.warn("sdk broker recovery requires session restart", {
|
|
7214
|
+
reason: "runtime_image_replaced",
|
|
7215
|
+
});
|
|
7216
|
+
return;
|
|
7217
|
+
}
|
|
7148
7218
|
if (brokerRecoveryStopped) return;
|
|
7149
7219
|
if (brokerRegistered) await (options.ensureBrokerImpl ?? ensureBroker)({ agentDir: options.agentDir });
|
|
7150
|
-
else
|
|
7220
|
+
else {
|
|
7221
|
+
// Optional registration logs and resolves on failure; recovery must still count it
|
|
7222
|
+
// as a failed attempt so the backoff holds instead of resetting every tick.
|
|
7223
|
+
await registerBroker();
|
|
7224
|
+
if (!brokerRegistered && !brokerRecoveryStopped)
|
|
7225
|
+
throw Object.assign(new Error("SDK broker registration did not complete."), {
|
|
7226
|
+
code: "registration_incomplete",
|
|
7227
|
+
});
|
|
7228
|
+
}
|
|
7151
7229
|
})();
|
|
7152
7230
|
brokerRecoveryInFlight = recovery;
|
|
7153
7231
|
try {
|
|
7154
7232
|
await recovery;
|
|
7233
|
+
if (brokerRecoveryRestartRequired || brokerRecoveryStopped) return;
|
|
7234
|
+
brokerRecoveryBackoff.recordSuccess(options.agentDir);
|
|
7155
7235
|
} catch (error) {
|
|
7156
|
-
|
|
7236
|
+
const shouldContinue = brokerRecoveryBackoff.recordFailure(options.agentDir);
|
|
7237
|
+
if (!shouldContinue) {
|
|
7238
|
+
// Max recovery attempts reached; mark that restart is required.
|
|
7239
|
+
brokerRecoveryRestartRequired = true;
|
|
7240
|
+
stopBrokerRecovery();
|
|
7241
|
+
logger.warn("sdk broker recovery max attempts exceeded", {
|
|
7242
|
+
code: brokerDiagnosticCode(error),
|
|
7243
|
+
});
|
|
7244
|
+
} else {
|
|
7245
|
+
logger.warn("sdk broker recovery unavailable", {
|
|
7246
|
+
code: brokerDiagnosticCode(error),
|
|
7247
|
+
backoffWaitMs: brokerRecoveryBackoff.getBackoffWaitMs(options.agentDir),
|
|
7248
|
+
});
|
|
7249
|
+
}
|
|
7157
7250
|
} finally {
|
|
7158
7251
|
if (brokerRecoveryInFlight === recovery) brokerRecoveryInFlight = undefined;
|
|
7159
7252
|
}
|
|
@@ -7210,6 +7303,9 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
7210
7303
|
try {
|
|
7211
7304
|
publishedEndpointUrl = (await runtime.start()).url;
|
|
7212
7305
|
await registerBroker();
|
|
7306
|
+
// Await the startup runtime image capture before arming recovery.
|
|
7307
|
+
// This ensures replacement detection works correctly on the first recovery check.
|
|
7308
|
+
await startupImageCapture;
|
|
7213
7309
|
if (!brokerRecoveryStopped) startBrokerRecovery();
|
|
7214
7310
|
} catch (error) {
|
|
7215
7311
|
active = undefined;
|
|
@@ -97,8 +97,13 @@ export class PromptActivity {
|
|
|
97
97
|
#awaitingModel = false;
|
|
98
98
|
|
|
99
99
|
/** Folds one inbound frame's lifecycle event into the in-flight tool and model state. */
|
|
100
|
-
observe(
|
|
101
|
-
|
|
100
|
+
observe(
|
|
101
|
+
eventType: string | undefined,
|
|
102
|
+
toolCallId: string | undefined,
|
|
103
|
+
messageRole: string | undefined,
|
|
104
|
+
messageHasToolCall = false,
|
|
105
|
+
): void {
|
|
106
|
+
this.#observeInference(eventType, messageRole, messageHasToolCall);
|
|
102
107
|
if (toolCallId === undefined) return;
|
|
103
108
|
if (eventType === "tool_execution_start" || eventType === "tool_execution_update") this.#running.add(toolCallId);
|
|
104
109
|
else if (eventType === "tool_execution_end") this.#running.delete(toolCallId);
|
|
@@ -110,7 +115,11 @@ export class PromptActivity {
|
|
|
110
115
|
* re-invocation that carries a tool result back — until the first frame of that
|
|
111
116
|
* response proves the model answered.
|
|
112
117
|
*/
|
|
113
|
-
#observeInference(
|
|
118
|
+
#observeInference(
|
|
119
|
+
eventType: string | undefined,
|
|
120
|
+
messageRole: string | undefined,
|
|
121
|
+
messageHasToolCall: boolean,
|
|
122
|
+
): void {
|
|
114
123
|
if (eventType === "agent_start" || eventType === "tool_execution_end") {
|
|
115
124
|
this.#awaitingModel = true;
|
|
116
125
|
return;
|
|
@@ -132,7 +141,11 @@ export class PromptActivity {
|
|
|
132
141
|
// Providers that do not stream emit no `message_update`, so the assistant message
|
|
133
142
|
// itself is the first proof. `message_start`/`message_end` also carry the user and
|
|
134
143
|
// tool-result messages the host echoes back, which prove nothing about the model.
|
|
135
|
-
if (
|
|
144
|
+
if (
|
|
145
|
+
(eventType === "message_start" || eventType === "message_end") &&
|
|
146
|
+
messageRole === "assistant" &&
|
|
147
|
+
!messageHasToolCall
|
|
148
|
+
)
|
|
136
149
|
this.#awaitingModel = false;
|
|
137
150
|
}
|
|
138
151
|
|
|
@@ -24425,8 +24425,15 @@ export class AgentSession {
|
|
|
24425
24425
|
if (!managedFallback && assistantMessageHasVisibleOrToolContent(message)) {
|
|
24426
24426
|
return false;
|
|
24427
24427
|
}
|
|
24428
|
+
const canReplayUnexpectedSocketClose =
|
|
24429
|
+
classification === "transient" &&
|
|
24430
|
+
isUnexpectedSocketCloseMessage(message.errorMessage ?? "") &&
|
|
24431
|
+
!hasBareDefaultRetryDisqualifyingFacts(message) &&
|
|
24432
|
+
this.#hasCleanRetryReplaySafety;
|
|
24428
24433
|
// Bare defaults retain their narrow watchdog and provider capacity-overload
|
|
24429
|
-
// admissions. A
|
|
24434
|
+
// admissions. A content-free unexpected socket close is replay-safe when
|
|
24435
|
+
// the attempt has no conflicting facts and the retry scope is clean. A
|
|
24436
|
+
// first-event timeout adds the typed, content-free,
|
|
24430
24437
|
// current-clean-scope requirement above; other transient watchdogs preserve
|
|
24431
24438
|
// legacy behavior. A provider overload is admitted only from that provider's
|
|
24432
24439
|
// own typed overload code on a content-free attempt carrying no conflicting
|
|
@@ -24435,12 +24442,16 @@ export class AgentSession {
|
|
|
24435
24442
|
if (!managedFallback && !legacyRetryConfigured && !canReplayRotatedCredential && !canReplayEmptyResponse) {
|
|
24436
24443
|
if (
|
|
24437
24444
|
(!canReplayProviderOverload &&
|
|
24445
|
+
!canReplayUnexpectedSocketClose &&
|
|
24438
24446
|
!this.#isTypedFirstEventTimeout(message) &&
|
|
24439
24447
|
!messageOnlyWatchdogTimeout &&
|
|
24440
24448
|
(hasBareDefaultRetryDisqualifyingFacts(message) ||
|
|
24441
24449
|
(classification !== "transient" && classification !== "first_event_timeout") ||
|
|
24442
24450
|
!BARE_DEFAULT_WATCHDOG_ERROR.test(message.errorMessage ?? ""))) ||
|
|
24443
|
-
(!firstEventTimeout &&
|
|
24451
|
+
(!firstEventTimeout &&
|
|
24452
|
+
!messageOnlyWatchdogTimeout &&
|
|
24453
|
+
!canReplayUnexpectedSocketClose &&
|
|
24454
|
+
!this.#hasCleanRetryReplaySafety)
|
|
24444
24455
|
) {
|
|
24445
24456
|
return false;
|
|
24446
24457
|
}
|
|
@@ -24449,6 +24460,7 @@ export class AgentSession {
|
|
|
24449
24460
|
!managedFallback &&
|
|
24450
24461
|
classification === "transient" &&
|
|
24451
24462
|
!canReplayProviderOverload &&
|
|
24463
|
+
!canReplayUnexpectedSocketClose &&
|
|
24452
24464
|
!this.#isIdleStreamStallErrorMessage(message.errorMessage ?? "");
|
|
24453
24465
|
|
|
24454
24466
|
const failedSelector = managedFallback ? controller.currentSelector() : undefined;
|
|
@@ -165,6 +165,27 @@ function isRetryableReplacementReceiptCleanupError(error: unknown): boolean {
|
|
|
165
165
|
return error instanceof Error && error.message === "managed_replace_receipt_cleanup_pending:io_error";
|
|
166
166
|
}
|
|
167
167
|
|
|
168
|
+
/**
|
|
169
|
+
* A receipt retirement slot is named only from the receipt and predecessor
|
|
170
|
+
* identities, so every process sharing a managed scope — the publisher retiring
|
|
171
|
+
* its own receipt and each peer reconciler that listed it — claims the same
|
|
172
|
+
* slot. Native exact-unlink reports `quarantine_collision` from its no-replace
|
|
173
|
+
* claim before any rename, so a collision that carries no detached or retained
|
|
174
|
+
* path proves only that a peer owns the retirement right now: this process
|
|
175
|
+
* changed nothing, the receipt stays where a later reconciliation finds it, and
|
|
176
|
+
* the unrelated mutation that triggered the scan must not fail.
|
|
177
|
+
*/
|
|
178
|
+
function isPeerOwnedReceiptRetirement(result: NativeExactUnlinkResult): boolean {
|
|
179
|
+
return (
|
|
180
|
+
!result.ok &&
|
|
181
|
+
result.code === "quarantine_collision" &&
|
|
182
|
+
result.detachedPath === undefined &&
|
|
183
|
+
result.retainedSuccessorPath === undefined &&
|
|
184
|
+
result.retainedPlaceholderPath === undefined &&
|
|
185
|
+
result.retainedUnknownPath === undefined
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
|
|
168
189
|
/** A same-filesystem rename updates the moved root's ctime but no other tree identity. */
|
|
169
190
|
function sameDirectoryTreeSnapshotAfterRename(
|
|
170
191
|
left: NativeDirectoryTreeSnapshot,
|
|
@@ -1622,6 +1643,7 @@ export class ManagedSessionDescendantStore {
|
|
|
1622
1643
|
sha256: placeholder.identity.sha256,
|
|
1623
1644
|
quarantineName,
|
|
1624
1645
|
});
|
|
1646
|
+
if (isPeerOwnedReceiptRetirement(removed)) return true;
|
|
1625
1647
|
if (
|
|
1626
1648
|
(!exactUnlinkCompleted(removed) && removed.code !== "not_found") ||
|
|
1627
1649
|
removed.retainedSuccessorPath !== undefined ||
|
|
@@ -1660,6 +1682,7 @@ export class ManagedSessionDescendantStore {
|
|
|
1660
1682
|
detachOnly: true,
|
|
1661
1683
|
quarantineName,
|
|
1662
1684
|
});
|
|
1685
|
+
if (isPeerOwnedReceiptRetirement(detached)) return;
|
|
1663
1686
|
if (
|
|
1664
1687
|
!detached.ok &&
|
|
1665
1688
|
detached.code === "not_found" &&
|
package/src/tools/bash.ts
CHANGED
|
@@ -10,10 +10,26 @@ import { type BashArtifactSaveResult, type BashResult, executeBash } from "../ex
|
|
|
10
10
|
|
|
11
11
|
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
12
12
|
import { buildGjcRuntimeSessionEnv } from "../gjc-runtime/goal-mode-request";
|
|
13
|
+
import {
|
|
14
|
+
MANAGED_OWNER_PREDECESSOR_GENERATION_ENV,
|
|
15
|
+
MANAGED_OWNER_PREDECESSOR_INCARNATION_ENV,
|
|
16
|
+
MANAGED_OWNER_PREDECESSOR_RUN_ID_ENV,
|
|
17
|
+
MANAGED_OWNER_PREDECESSOR_TOKEN_ENV,
|
|
18
|
+
MANAGED_OWNER_TRANSCRIPT_PATH_ENV,
|
|
19
|
+
} from "../gjc-runtime/managed-owner-admission";
|
|
20
|
+
import {
|
|
21
|
+
MANAGED_OWNER_CHILD_TOKEN_ENV,
|
|
22
|
+
MANAGED_OWNER_GENERATION_ENV,
|
|
23
|
+
MANAGED_OWNER_INCARNATION_ENV,
|
|
24
|
+
MANAGED_OWNER_RUN_ID_ENV,
|
|
25
|
+
MANAGED_OWNER_STATE_DIR_ENV,
|
|
26
|
+
} from "../gjc-runtime/managed-owner-supervisor";
|
|
13
27
|
import {
|
|
14
28
|
GJC_RALPLAN_ARTIFACT_ENV,
|
|
15
29
|
GJC_RESTRICTED_ROLE_AGENT_BASH_ENV,
|
|
16
30
|
} from "../gjc-runtime/restricted-role-agent-bash";
|
|
31
|
+
import { GJC_TMUX_OWNER_SERVER_KEY_ENV } from "../gjc-runtime/session-state-sidecar";
|
|
32
|
+
import { GJC_TMUX_LAUNCHED_ENV } from "../gjc-runtime/windows-powershell-command";
|
|
17
33
|
import { InternalUrlRouter } from "../internal-urls";
|
|
18
34
|
import { truncateToVisualLines } from "../modes/components/visual-truncate";
|
|
19
35
|
import { highlightCode, type Theme } from "../modes/theme/theme";
|
|
@@ -94,6 +110,27 @@ const ARTIFACT_SAVE_DIAGNOSTIC_MAX_BYTES = 256;
|
|
|
94
110
|
const BASH_ENV_NAME_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
95
111
|
const MASTER_CAPABILITY_ENV = "GJC_MASTER_CAPABILITY";
|
|
96
112
|
const MASTER_OWNER_SESSION_ENV = "GJC_MASTER_OWNER_SESSION_ID";
|
|
113
|
+
// Managed-owner env vars that must be scrubbed from child processes.
|
|
114
|
+
// Exported for testing the env scrubbing behavior.
|
|
115
|
+
export const MANAGED_OWNER_BASH_ENV = [
|
|
116
|
+
MANAGED_OWNER_STATE_DIR_ENV,
|
|
117
|
+
MANAGED_OWNER_GENERATION_ENV,
|
|
118
|
+
MANAGED_OWNER_RUN_ID_ENV,
|
|
119
|
+
MANAGED_OWNER_INCARNATION_ENV,
|
|
120
|
+
MANAGED_OWNER_CHILD_TOKEN_ENV,
|
|
121
|
+
MANAGED_OWNER_PREDECESSOR_TOKEN_ENV,
|
|
122
|
+
MANAGED_OWNER_PREDECESSOR_GENERATION_ENV,
|
|
123
|
+
MANAGED_OWNER_PREDECESSOR_RUN_ID_ENV,
|
|
124
|
+
MANAGED_OWNER_PREDECESSOR_INCARNATION_ENV,
|
|
125
|
+
MANAGED_OWNER_TRANSCRIPT_PATH_ENV,
|
|
126
|
+
// The rest of the tmux-owner tuple (tmux-sessions.ts managedEnvironment): leaving
|
|
127
|
+
// either behind makes ownerTerminalContextFromEnvironment() report "invalid" in a
|
|
128
|
+
// nested gjc, because a server key or GJC_TMUX_LAUNCHED=1 without generation/state
|
|
129
|
+
// dir is an incomplete owner context.
|
|
130
|
+
GJC_TMUX_OWNER_SERVER_KEY_ENV,
|
|
131
|
+
GJC_TMUX_LAUNCHED_ENV,
|
|
132
|
+
] as const;
|
|
133
|
+
|
|
97
134
|
const COORDINATOR_ONLY_BASH_ENV = [
|
|
98
135
|
"GJC_COORDINATOR_SESSION_STATE_FILE",
|
|
99
136
|
"GJC_COORDINATOR_SESSION_ID",
|
|
@@ -102,6 +139,8 @@ const COORDINATOR_ONLY_BASH_ENV = [
|
|
|
102
139
|
"GJC_COORDINATOR_SESSION_READINESS_FILE",
|
|
103
140
|
"GJC_COORDINATOR_SIDECAR_SIGNATURE_REQUIRED",
|
|
104
141
|
"GJC_COORDINATOR_SIDECAR_KEY_ID",
|
|
142
|
+
// Managed-owner env family from managed-owner-supervisor.ts and managed-owner-admission.ts
|
|
143
|
+
...MANAGED_OWNER_BASH_ENV,
|
|
105
144
|
] as const;
|
|
106
145
|
const DEFAULT_AUTO_BACKGROUND_THRESHOLD_MS = 60_000;
|
|
107
146
|
const ACP_RELEASE_TIMEOUT_MS = 1_000;
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { GrepOutputMode } from "@gajae-code/natives";
|
|
2
|
-
export interface GrepCommandArgs {
|
|
3
|
-
pattern: string;
|
|
4
|
-
path: string;
|
|
5
|
-
glob?: string;
|
|
6
|
-
limit: number;
|
|
7
|
-
context: number;
|
|
8
|
-
mode: GrepOutputMode;
|
|
9
|
-
gitignore: boolean;
|
|
10
|
-
}
|
|
11
|
-
/**
|
|
12
|
-
* Parse grep subcommand arguments.
|
|
13
|
-
* Returns undefined if not a grep command.
|
|
14
|
-
*/
|
|
15
|
-
export declare function parseGrepArgs(args: string[]): GrepCommandArgs | undefined;
|
|
16
|
-
export declare function runGrepCommand(cmd: GrepCommandArgs): Promise<void>;
|
|
17
|
-
export declare function printGrepHelp(): void;
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
export interface ShellCommandArgs {
|
|
2
|
-
cwd?: string;
|
|
3
|
-
timeoutMs?: number;
|
|
4
|
-
noSnapshot?: boolean;
|
|
5
|
-
}
|
|
6
|
-
export declare function parseShellArgs(args: string[]): ShellCommandArgs | undefined;
|
|
7
|
-
export declare function runShellCommand(cmd: ShellCommandArgs): Promise<void>;
|
|
8
|
-
export declare function printShellHelp(): void;
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { Command } from "@gajae-code/utils/cli";
|
|
2
|
-
export default class Grep extends Command {
|
|
3
|
-
static description: string;
|
|
4
|
-
static args: {
|
|
5
|
-
pattern: import("@gajae-code/utils/cli").ArgDescriptor & {
|
|
6
|
-
description: string;
|
|
7
|
-
required: false;
|
|
8
|
-
};
|
|
9
|
-
path: import("@gajae-code/utils/cli").ArgDescriptor & {
|
|
10
|
-
description: string;
|
|
11
|
-
required: false;
|
|
12
|
-
};
|
|
13
|
-
};
|
|
14
|
-
static flags: {
|
|
15
|
-
glob: import("@gajae-code/utils/cli").FlagDescriptor<"string"> & {
|
|
16
|
-
char: string;
|
|
17
|
-
description: string;
|
|
18
|
-
};
|
|
19
|
-
limit: import("@gajae-code/utils/cli").FlagDescriptor<"integer"> & {
|
|
20
|
-
char: string;
|
|
21
|
-
description: string;
|
|
22
|
-
default: number;
|
|
23
|
-
};
|
|
24
|
-
context: import("@gajae-code/utils/cli").FlagDescriptor<"integer"> & {
|
|
25
|
-
char: string;
|
|
26
|
-
description: string;
|
|
27
|
-
default: number;
|
|
28
|
-
};
|
|
29
|
-
files: import("@gajae-code/utils/cli").FlagDescriptor<"boolean"> & {
|
|
30
|
-
char: string;
|
|
31
|
-
description: string;
|
|
32
|
-
};
|
|
33
|
-
count: import("@gajae-code/utils/cli").FlagDescriptor<"boolean"> & {
|
|
34
|
-
char: string;
|
|
35
|
-
description: string;
|
|
36
|
-
};
|
|
37
|
-
"no-gitignore": import("@gajae-code/utils/cli").FlagDescriptor<"boolean"> & {
|
|
38
|
-
description: string;
|
|
39
|
-
};
|
|
40
|
-
};
|
|
41
|
-
run(): Promise<void>;
|
|
42
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Interactive shell console.
|
|
3
|
-
*/
|
|
4
|
-
import { Command } from "@gajae-code/utils/cli";
|
|
5
|
-
export default class Shell extends Command {
|
|
6
|
-
static description: string;
|
|
7
|
-
static flags: {
|
|
8
|
-
cwd: import("@gajae-code/utils/cli").FlagDescriptor<"string"> & {
|
|
9
|
-
char: string;
|
|
10
|
-
description: string;
|
|
11
|
-
};
|
|
12
|
-
timeout: import("@gajae-code/utils/cli").FlagDescriptor<"integer"> & {
|
|
13
|
-
char: string;
|
|
14
|
-
description: string;
|
|
15
|
-
};
|
|
16
|
-
"no-snapshot": import("@gajae-code/utils/cli").FlagDescriptor<"boolean"> & {
|
|
17
|
-
description: string;
|
|
18
|
-
};
|
|
19
|
-
};
|
|
20
|
-
run(): Promise<void>;
|
|
21
|
-
}
|
package/src/cli/grep-cli.ts
DELETED
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Grep CLI command handlers.
|
|
3
|
-
*
|
|
4
|
-
* Handles `gjc grep` subcommand for testing grep tool on Windows.
|
|
5
|
-
*/
|
|
6
|
-
import * as path from "node:path";
|
|
7
|
-
import { GrepOutputMode, grep } from "@gajae-code/natives";
|
|
8
|
-
import { APP_NAME } from "@gajae-code/utils";
|
|
9
|
-
import chalk from "chalk";
|
|
10
|
-
|
|
11
|
-
export interface GrepCommandArgs {
|
|
12
|
-
pattern: string;
|
|
13
|
-
path: string;
|
|
14
|
-
glob?: string;
|
|
15
|
-
limit: number;
|
|
16
|
-
context: number;
|
|
17
|
-
mode: GrepOutputMode;
|
|
18
|
-
gitignore: boolean;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Parse grep subcommand arguments.
|
|
23
|
-
* Returns undefined if not a grep command.
|
|
24
|
-
*/
|
|
25
|
-
export function parseGrepArgs(args: string[]): GrepCommandArgs | undefined {
|
|
26
|
-
if (args.length === 0 || args[0] !== "grep") {
|
|
27
|
-
return undefined;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const result: GrepCommandArgs = {
|
|
31
|
-
pattern: "",
|
|
32
|
-
path: ".",
|
|
33
|
-
limit: 20,
|
|
34
|
-
context: 2,
|
|
35
|
-
mode: GrepOutputMode.Content,
|
|
36
|
-
gitignore: true,
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
const positional: string[] = [];
|
|
40
|
-
|
|
41
|
-
for (let i = 1; i < args.length; i++) {
|
|
42
|
-
const arg = args[i];
|
|
43
|
-
if (arg === "--glob" || arg === "-g") {
|
|
44
|
-
result.glob = args[++i];
|
|
45
|
-
} else if (arg === "--limit" || arg === "-l") {
|
|
46
|
-
result.limit = parseInt(args[++i], 10);
|
|
47
|
-
} else if (arg === "--context" || arg === "-C") {
|
|
48
|
-
result.context = parseInt(args[++i], 10);
|
|
49
|
-
} else if (arg === "--files" || arg === "-f") {
|
|
50
|
-
result.mode = GrepOutputMode.FilesWithMatches;
|
|
51
|
-
} else if (arg === "--count" || arg === "-c") {
|
|
52
|
-
result.mode = GrepOutputMode.Count;
|
|
53
|
-
} else if (arg === "--no-gitignore") {
|
|
54
|
-
result.gitignore = false;
|
|
55
|
-
} else if (!arg.startsWith("-")) {
|
|
56
|
-
positional.push(arg);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
if (positional.length >= 1) {
|
|
61
|
-
result.pattern = positional[0];
|
|
62
|
-
}
|
|
63
|
-
if (positional.length >= 2) {
|
|
64
|
-
result.path = positional[1];
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
return result;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
export async function runGrepCommand(cmd: GrepCommandArgs): Promise<void> {
|
|
71
|
-
if (!cmd.pattern) {
|
|
72
|
-
console.error(chalk.red("Error: Pattern is required"));
|
|
73
|
-
process.exit(1);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const searchPath = path.resolve(cmd.path);
|
|
77
|
-
console.log(chalk.dim(`Searching in: ${searchPath}`));
|
|
78
|
-
console.log(chalk.dim(`Pattern: ${cmd.pattern}`));
|
|
79
|
-
console.log(
|
|
80
|
-
chalk.dim(`Mode: ${cmd.mode}, Limit: ${cmd.limit}, Context: ${cmd.context}, Gitignore: ${cmd.gitignore}`),
|
|
81
|
-
);
|
|
82
|
-
|
|
83
|
-
console.log("");
|
|
84
|
-
|
|
85
|
-
try {
|
|
86
|
-
const result = await grep({
|
|
87
|
-
pattern: cmd.pattern,
|
|
88
|
-
path: searchPath,
|
|
89
|
-
glob: cmd.glob,
|
|
90
|
-
mode: cmd.mode,
|
|
91
|
-
maxCount: cmd.limit,
|
|
92
|
-
context: cmd.mode === GrepOutputMode.Content ? cmd.context : undefined,
|
|
93
|
-
hidden: true,
|
|
94
|
-
gitignore: cmd.gitignore,
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
console.log(chalk.green(`Total matches: ${result.totalMatches}`));
|
|
98
|
-
console.log(chalk.green(`Files with matches: ${result.filesWithMatches}`));
|
|
99
|
-
console.log(chalk.green(`Files searched: ${result.filesSearched}`));
|
|
100
|
-
if (result.limitReached) {
|
|
101
|
-
console.log(chalk.yellow(`Result limit reached (${cmd.limit} matches). Use --limit to show more.`));
|
|
102
|
-
}
|
|
103
|
-
console.log("");
|
|
104
|
-
|
|
105
|
-
for (const match of result.matches) {
|
|
106
|
-
const displayPath = match.path.replace(/\\/g, "/");
|
|
107
|
-
|
|
108
|
-
if (cmd.mode === GrepOutputMode.Content) {
|
|
109
|
-
if (match.contextBefore) {
|
|
110
|
-
for (const ctx of match.contextBefore) {
|
|
111
|
-
console.log(chalk.dim(`${displayPath}-${ctx.lineNumber}- ${ctx.line}`));
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
console.log(`${chalk.cyan(displayPath)}:${chalk.yellow(String(match.lineNumber))}: ${match.line}`);
|
|
115
|
-
if (match.contextAfter) {
|
|
116
|
-
for (const ctx of match.contextAfter) {
|
|
117
|
-
console.log(chalk.dim(`${displayPath}-${ctx.lineNumber}- ${ctx.line}`));
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
console.log("");
|
|
121
|
-
} else if (cmd.mode === GrepOutputMode.Count) {
|
|
122
|
-
console.log(`${chalk.cyan(displayPath)}: ${match.matchCount ?? 0} matches`);
|
|
123
|
-
} else {
|
|
124
|
-
console.log(chalk.cyan(displayPath));
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
} catch (err) {
|
|
128
|
-
console.error(chalk.red(`Error: ${err instanceof Error ? err.message : String(err)}`));
|
|
129
|
-
process.exit(1);
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
export function printGrepHelp(): void {
|
|
134
|
-
console.log(`${chalk.bold(`${APP_NAME} grep`)} - Test grep tool
|
|
135
|
-
|
|
136
|
-
${chalk.bold("Usage:")}
|
|
137
|
-
${APP_NAME} grep <pattern> [path] [options]
|
|
138
|
-
|
|
139
|
-
${chalk.bold("Arguments:")}
|
|
140
|
-
pattern Regex pattern to search for
|
|
141
|
-
path Directory or file to search (default: .)
|
|
142
|
-
|
|
143
|
-
${chalk.bold("Options:")}
|
|
144
|
-
-g, --glob <pattern> Filter files by glob pattern
|
|
145
|
-
-l, --limit <n> Max matches (default: 20)
|
|
146
|
-
-C, --context <n> Context lines (default: 2)
|
|
147
|
-
-f, --files Output file names only
|
|
148
|
-
-c, --count Output match counts per file
|
|
149
|
-
-h, --help Show this help
|
|
150
|
-
--no-gitignore Include files excluded by .gitignore
|
|
151
|
-
|
|
152
|
-
${chalk.bold("Environment:")}
|
|
153
|
-
PI_GREP_WORKERS=N Set filesystem walker workers (default 4, 0 = auto)
|
|
154
|
-
|
|
155
|
-
${chalk.bold("Examples:")}
|
|
156
|
-
${APP_NAME} grep "import" src/
|
|
157
|
-
${APP_NAME} grep "TODO" . --glob "*.ts"
|
|
158
|
-
${APP_NAME} grep "function" --files
|
|
159
|
-
`);
|
|
160
|
-
}
|