@gajae-code/coding-agent 0.18.10 → 0.18.12
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 +295 -0
- package/dist/types/async/job-manager.d.ts +7 -3
- package/dist/types/async/result-formatting.d.ts +2 -0
- package/dist/types/autoresearch/git.d.ts +1 -1
- package/dist/types/capability/fs.d.ts +2 -2
- package/dist/types/cli/accounts-cli.d.ts +2 -2
- package/dist/types/cli/doctor/install-repairs.d.ts +4 -1
- package/dist/types/cli/install-activation.d.ts +10 -4
- package/dist/types/cli/public-command-errors.d.ts +1 -0
- package/dist/types/cli/update-cli.d.ts +14 -1
- package/dist/types/cli-ordinary.d.ts +2 -2
- package/dist/types/commands/accounts.d.ts +2 -1
- package/dist/types/commands/migrate.d.ts +1 -1
- package/dist/types/commands/sdk.d.ts +5 -0
- package/dist/types/commit/changelog/index.d.ts +1 -0
- package/dist/types/config/autorouting-tier-map.d.ts +30 -0
- package/dist/types/config/config-hot-reload-trust.d.ts +12 -0
- package/dist/types/config/config-hot-reload.d.ts +39 -0
- package/dist/types/config/file-lock.d.ts +8 -1
- package/dist/types/config/model-discovery-manager.d.ts +2 -0
- package/dist/types/config/model-profile-activation.d.ts +62 -6
- package/dist/types/config/model-registry.d.ts +56 -5
- package/dist/types/config/model-resolver.d.ts +2 -0
- package/dist/types/config/models-config-schema.d.ts +10 -0
- package/dist/types/config/settings-schema.d.ts +46 -3
- package/dist/types/config/settings.d.ts +33 -1
- package/dist/types/dap/config.d.ts +8 -4
- package/dist/types/debug/system-info.d.ts +6 -1
- package/dist/types/discovery/mcp-compat.d.ts +3 -1
- package/dist/types/edit/modes/patch.d.ts +5 -0
- package/dist/types/edit/modes/replace.d.ts +3 -0
- package/dist/types/exec/bash-executor.d.ts +12 -0
- package/dist/types/gjc-runtime/managed-owner-admission.d.ts +3 -0
- package/dist/types/gjc-runtime/session-state-lock.d.ts +2 -0
- package/dist/types/gjc-runtime/state-runtime.d.ts +0 -2
- package/dist/types/migrate/adapters/codex.d.ts +2 -1
- package/dist/types/migrate/adapters/index.d.ts +10 -1
- package/dist/types/migrate/executor.d.ts +3 -1
- package/dist/types/migrate/types.d.ts +15 -3
- package/dist/types/modes/components/settings-selector.d.ts +6 -0
- package/dist/types/modes/controllers/selector-controller.d.ts +2 -0
- package/dist/types/modes/theme/theme.d.ts +1 -0
- package/dist/types/runtime-mcp/content-limits.d.ts +4 -0
- package/dist/types/runtime-mcp/manager.d.ts +7 -0
- package/dist/types/runtime-mcp/plugin-mcp-name-filter.d.ts +22 -0
- package/dist/types/runtime-mcp/tool-bridge.d.ts +4 -0
- package/dist/types/sdk/broker/broker-exit.d.ts +2 -2
- package/dist/types/sdk/broker/broker.d.ts +7 -1
- package/dist/types/sdk/broker/daemon-entry.d.ts +11 -1
- package/dist/types/sdk/broker/doctor-restart.d.ts +1 -1
- package/dist/types/sdk/broker/ensure.d.ts +136 -7
- package/dist/types/sdk/broker/hop.d.ts +36 -0
- package/dist/types/sdk/broker/lifecycle-ledger.d.ts +18 -10
- package/dist/types/sdk/broker/lock-host-identity.d.ts +5 -0
- package/dist/types/sdk/broker/session-index.d.ts +4 -1
- package/dist/types/sdk/bus/chat-command-policy.d.ts +2 -1
- package/dist/types/sdk/bus/index.d.ts +23 -0
- package/dist/types/sdk/bus/reconciliation-store.d.ts +10 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +3 -1
- package/dist/types/sdk/cli/session-cli.d.ts +0 -1
- package/dist/types/sdk/host/session-runtime.d.ts +32 -0
- package/dist/types/sdk/prompt-deadline-flush.d.ts +42 -4
- package/dist/types/sdk/prompt-deadline-manager.d.ts +2 -0
- package/dist/types/sdk/protocol/adapter-validation.d.ts +10 -4
- package/dist/types/sdk/reconciliation-extensions.d.ts +1 -1
- package/dist/types/sdk/session-list.d.ts +1 -1
- package/dist/types/session/agent-session.d.ts +58 -2
- package/dist/types/session/internal/managed-session-scope.d.ts +2 -2
- package/dist/types/session/internal/managed-session-storage.d.ts +1 -1
- package/dist/types/session/internal/native-publish-outcome.d.ts +1 -1
- package/dist/types/session/messages.d.ts +5 -1
- package/dist/types/session/session-manager.d.ts +13 -3
- package/dist/types/session/startup-auth-config.d.ts +24 -2
- package/dist/types/session/streaming-output.d.ts +11 -0
- package/dist/types/session/terminal-abort.d.ts +3 -1
- package/dist/types/session-import/service.d.ts +1 -1
- package/dist/types/skill-state/workflow-mutation-guard.d.ts +7 -3
- package/dist/types/slash-commands/helpers/provider-auth-status.d.ts +8 -0
- package/dist/types/task/executor.d.ts +19 -2
- package/dist/types/tools/ast-edit-write-path.d.ts +10 -0
- package/dist/types/tools/atomic-file-write.d.ts +9 -7
- package/dist/types/tools/bash-allowed-prefixes.d.ts +11 -0
- package/dist/types/tools/descriptors.d.ts +2 -0
- package/dist/types/tools/fetch.d.ts +1 -1
- package/dist/types/tools/index.d.ts +2 -0
- package/dist/types/tools/python.d.ts +1 -1
- package/dist/types/tools/report-issue.d.ts +41 -0
- package/dist/types/tools/subagent.d.ts +23 -0
- package/dist/types/web/scrapers/types.d.ts +8 -1
- package/dist/types/web/search/providers/insane.d.ts +7 -1
- package/dist/types/web/search/providers/searxng.d.ts +5 -0
- package/package.json +8 -8
- package/scripts/build-binary.ts +2 -1
- package/scripts/capture-subagent-compact-panel-showcase.ts +105 -0
- package/scripts/generate-docs-index.ts +8 -1
- package/scripts/generate-sdk-operation-inventory.ts +22 -4
- package/src/async/job-manager.ts +356 -56
- package/src/async/result-formatting.ts +40 -0
- package/src/autoresearch/git.ts +9 -2
- package/src/autoresearch/prompts/prompt-setup.md +1 -1
- package/src/capability/fs.ts +27 -19
- package/src/capability/index.ts +2 -2
- package/src/cli/accounts-cli.ts +176 -8
- package/src/cli/auth-broker-cli.ts +21 -11
- package/src/cli/doctor/install-repairs.ts +35 -5
- package/src/cli/fast-help.ts +1 -1
- package/src/cli/install-activation.ts +47 -4
- package/src/cli/migrate-cli.ts +7 -5
- package/src/cli/public-command-errors.ts +4 -0
- package/src/cli/root-flags.ts +2 -2
- package/src/cli/stats-cli.ts +9 -5
- package/src/cli/update-cli.ts +224 -7
- package/src/cli-ordinary.ts +7 -2
- package/src/commands/accounts.ts +10 -5
- package/src/commands/migrate.ts +3 -3
- package/src/commands/sdk.ts +199 -42
- package/src/commands/ultragoal.ts +16 -8
- package/src/commit/changelog/index.ts +39 -1
- package/src/commit/message.ts +10 -3
- package/src/config/autorouting-tier-map.ts +30 -0
- package/src/config/config-hot-reload-trust.ts +148 -0
- package/src/config/config-hot-reload.ts +734 -0
- package/src/config/file-lock.ts +216 -56
- package/src/config/model-bindings-applier.ts +85 -47
- package/src/config/model-discovery-manager.ts +10 -0
- package/src/config/model-profile-activation.ts +716 -113
- package/src/config/model-registry.ts +1334 -158
- package/src/config/model-resolver.ts +3 -1
- package/src/config/models-config-schema.ts +6 -0
- package/src/config/settings-schema.ts +60 -3
- package/src/config/settings.ts +458 -24
- package/src/coordinator-mcp/policy.ts +26 -1
- package/src/crash/sanitize.ts +4 -0
- package/src/customization/import.ts +3 -1
- package/src/dap/config.ts +31 -11
- package/src/dap/defaults.json +1 -1
- package/src/debug/crash-diagnostics.ts +2 -1
- package/src/debug/report-bundle.ts +19 -0
- package/src/debug/system-info.ts +8 -3
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/shared/base-url.ts +21 -7
- package/src/discovery/agents-md.ts +1 -1
- package/src/discovery/helpers.ts +1 -2
- package/src/discovery/mcp-compat.ts +120 -17
- package/src/edit/apply-patch/index.ts +12 -1
- package/src/edit/index.ts +13 -17
- package/src/edit/modes/patch.ts +187 -8
- package/src/edit/modes/replace.ts +136 -0
- package/src/eval/js/context-manager.ts +29 -3
- package/src/eval/js/worker-entry.ts +51 -18
- package/src/eval/py/executor.ts +14 -4
- package/src/eval/py/runner.py +144 -5
- package/src/exec/bash-executor.ts +81 -4
- package/src/exec/bash-shell-guardian.ts +4 -1
- package/src/exec/bash-shell-supervisor.ts +8 -4
- package/src/exec/isolated-shell.ts +5 -1
- package/src/export/html/index.ts +46 -6
- package/src/extensibility/custom-tools/wrapper.ts +3 -1
- package/src/extensibility/extensions/loader.ts +8 -1
- package/src/extensibility/plugins/marketplace/fetcher.ts +2 -2
- package/src/gjc-runtime/gc-runtime.ts +33 -6
- package/src/gjc-runtime/launch-tmux.ts +7 -2
- package/src/gjc-runtime/launch-worktree.test.ts +148 -0
- package/src/gjc-runtime/launch-worktree.ts +101 -14
- package/src/gjc-runtime/managed-owner-admission.ts +46 -2
- package/src/gjc-runtime/session-resolution.ts +38 -6
- package/src/gjc-runtime/session-state-lock.ts +59 -14
- package/src/gjc-runtime/state-runtime.ts +23 -19
- package/src/hooks/native-skill-hook.ts +14 -11
- package/src/internal-urls/docs-index.generated.ts +14 -13
- package/src/internal-urls/local-protocol.ts +142 -8
- package/src/lsp/index.ts +29 -0
- package/src/lsp/workspace-path.ts +23 -0
- package/src/migrate/action-planner.ts +57 -0
- package/src/migrate/adapters/claude-code.ts +23 -4
- package/src/migrate/adapters/codex.ts +22 -4
- package/src/migrate/adapters/index.ts +22 -4
- package/src/migrate/adapters/opencode.ts +6 -1
- package/src/migrate/executor.ts +84 -7
- package/src/migrate/report.ts +19 -2
- package/src/migrate/types.ts +14 -4
- package/src/modes/DESIGN.md +51 -0
- package/src/modes/acp/permission-mode.ts +18 -1
- package/src/modes/components/assistant-message.ts +55 -1
- package/src/modes/components/settings-selector.ts +103 -37
- package/src/modes/controllers/command-controller.ts +38 -2
- package/src/modes/controllers/plan-mode-controller.ts +17 -7
- package/src/modes/controllers/selector-controller.ts +62 -6
- package/src/modes/interactive-mode.ts +68 -0
- package/src/modes/theme/theme.ts +15 -0
- package/src/prompts/tools/debug.md +1 -1
- package/src/prompts/tools/read.md +1 -1
- package/src/prompts/tools/report-issue.md +13 -0
- package/src/prompts/tools/task.md +1 -1
- package/src/runtime-credential-selector.ts +2 -2
- package/src/runtime-mcp/content-limits.ts +7 -0
- package/src/runtime-mcp/manager-pool.test.ts +5 -4
- package/src/runtime-mcp/manager.ts +32 -0
- package/src/runtime-mcp/oauth-flow.ts +32 -6
- package/src/runtime-mcp/plugin-mcp-name-filter.ts +62 -0
- package/src/runtime-mcp/render.ts +16 -1
- package/src/runtime-mcp/tool-bridge.ts +126 -17
- package/src/sdk/broker/broker-exit.ts +8 -5
- package/src/sdk/broker/broker.ts +342 -82
- package/src/sdk/broker/daemon-entry.ts +338 -116
- package/src/sdk/broker/doctor-restart.ts +2 -1
- package/src/sdk/broker/ensure.ts +1143 -265
- package/src/sdk/broker/hop.ts +274 -0
- package/src/sdk/broker/lifecycle-ledger.ts +35 -11
- package/src/sdk/broker/lock-host-identity.ts +29 -0
- package/src/sdk/broker/session-index.ts +62 -12
- package/src/sdk/bus/chat-command-policy.ts +16 -3
- package/src/sdk/bus/index.ts +52 -21
- package/src/sdk/bus/reconciliation-store.ts +201 -8
- package/src/sdk/bus/telegram-daemon-contract.ts +3 -1
- package/src/sdk/cli/session-cli.ts +21 -6
- package/src/sdk/host/session-runtime.test.ts +118 -23
- package/src/sdk/host/session-runtime.ts +352 -47
- package/src/sdk/lifecycle/service.ts +1 -0
- package/src/sdk/prompt-deadline-flush.ts +78 -5
- package/src/sdk/prompt-deadline-manager.ts +68 -14
- package/src/sdk/protocol/adapter-validation.ts +53 -11
- package/src/sdk/protocol/operation-inventory.generated.json +101 -2
- package/src/sdk/reconciliation-extensions.ts +1 -0
- package/src/sdk/session-list.ts +1 -0
- package/src/sdk/session.ts +114 -61
- package/src/session/agent-session.ts +1043 -81
- package/src/session/internal/managed-session-scope.ts +126 -39
- package/src/session/internal/managed-session-storage.ts +14 -11
- package/src/session/internal/native-publish-outcome.ts +5 -1
- package/src/session/messages.ts +47 -6
- package/src/session/session-manager.ts +236 -14
- package/src/session/startup-auth-config.ts +116 -8
- package/src/session/streaming-output.ts +262 -4
- package/src/session/terminal-abort.ts +14 -2
- package/src/session-import/service.ts +1 -0
- package/src/skill-state/workflow-mutation-guard.ts +491 -32
- package/src/slash-commands/builtin-registry.ts +15 -3
- package/src/slash-commands/helpers/credential-switch.ts +10 -5
- package/src/slash-commands/helpers/provider-auth-status.ts +456 -0
- package/src/task/executor.ts +103 -12
- package/src/task/index.ts +389 -175
- package/src/tools/ast-edit-write-path.ts +64 -0
- package/src/tools/ast-edit.ts +18 -1
- package/src/tools/atomic-file-write.ts +46 -15
- package/src/tools/auto-generated-guard.ts +3 -0
- package/src/tools/bash-allowed-prefixes.ts +153 -5
- package/src/tools/bash-interactive.ts +14 -2
- package/src/tools/bash.ts +14 -0
- package/src/tools/debug.ts +7 -6
- package/src/tools/descriptors.test.ts +44 -0
- package/src/tools/descriptors.ts +34 -2
- package/src/tools/fetch.ts +3 -2
- package/src/tools/index.ts +2 -0
- package/src/tools/output-meta.ts +23 -2
- package/src/tools/python.ts +1 -1
- package/src/tools/read.ts +17 -35
- package/src/tools/report-issue.ts +184 -0
- package/src/tools/subagent-render.ts +114 -12
- package/src/tools/subagent.ts +108 -1
- package/src/tools/todo-write.ts +41 -3
- package/src/tools/tool-catalog.generated.ts +31 -3
- package/src/tools/write.ts +35 -3
- package/src/utils/commit-message-generator.ts +9 -2
- package/src/utils/file-mentions.ts +29 -1
- package/src/utils/title-generator.ts +7 -2
- package/src/web/scrapers/index.ts +3 -1
- package/src/web/scrapers/types.ts +8 -0
- package/src/web/scrapers/youtube.ts +3 -2
- package/src/web/search/providers/insane.ts +91 -28
- package/src/web/search/providers/searxng.ts +62 -10
package/src/eval/py/runner.py
CHANGED
|
@@ -29,6 +29,7 @@ import asyncio
|
|
|
29
29
|
import ast
|
|
30
30
|
import base64
|
|
31
31
|
import builtins
|
|
32
|
+
import codecs
|
|
32
33
|
import inspect
|
|
33
34
|
import io
|
|
34
35
|
import json
|
|
@@ -49,9 +50,14 @@ from typing import Any, Callable
|
|
|
49
50
|
# Frame writer
|
|
50
51
|
# ---------------------------------------------------------------------------
|
|
51
52
|
|
|
52
|
-
_RAW_STDOUT = sys.__stdout__
|
|
53
53
|
_RAW_STDERR = sys.__stderr__
|
|
54
54
|
_OUT_LOCK = threading.Lock()
|
|
55
|
+
_PROTO_FD: int | None = None
|
|
56
|
+
_CAP_LOCK = threading.Lock()
|
|
57
|
+
_FLUSH_EVENT = threading.Event()
|
|
58
|
+
_FLUSH_TOKEN: bytes | None = None
|
|
59
|
+
_CAPTURED = b""
|
|
60
|
+
_CAPTURE_DECODER = codecs.getincrementaldecoder("utf-8")(errors="replace")
|
|
55
61
|
|
|
56
62
|
|
|
57
63
|
def _json_default(o: Any) -> Any:
|
|
@@ -61,13 +67,141 @@ def _json_default(o: Any) -> Any:
|
|
|
61
67
|
return f"<unrepr {type(o).__name__}>"
|
|
62
68
|
|
|
63
69
|
|
|
70
|
+
def _write_all(fd: int, data: bytes) -> None:
|
|
71
|
+
view = memoryview(data)
|
|
72
|
+
while len(view):
|
|
73
|
+
written = os.write(fd, view)
|
|
74
|
+
if written <= 0:
|
|
75
|
+
raise OSError(0, "protocol write returned no bytes")
|
|
76
|
+
view = view[written:]
|
|
77
|
+
|
|
78
|
+
|
|
64
79
|
def _emit(frame: dict) -> None:
|
|
65
80
|
"""Serialize a frame and write it to the host as a single NDJSON line."""
|
|
66
|
-
|
|
81
|
+
payload = (json.dumps(frame, ensure_ascii=False, default=_json_default) + "\n").encode("utf-8")
|
|
82
|
+
fd = _PROTO_FD if _PROTO_FD is not None else 1
|
|
67
83
|
with _OUT_LOCK:
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
84
|
+
_write_all(fd, payload)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _token_suffix_len(buf: bytes, token: bytes) -> int:
|
|
88
|
+
max_n = min(len(buf), len(token) - 1)
|
|
89
|
+
for size in range(max_n, 0, -1):
|
|
90
|
+
if buf.endswith(token[:size]):
|
|
91
|
+
return size
|
|
92
|
+
return 0
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _take_captured_locked() -> tuple[list[bytes], bool]:
|
|
96
|
+
"""Split captured fd-1 bytes. A flush token is a delimiter, not output."""
|
|
97
|
+
global _CAPTURED, _FLUSH_TOKEN
|
|
98
|
+
pieces: list[bytes] = []
|
|
99
|
+
flushed = False
|
|
100
|
+
while True:
|
|
101
|
+
token = _FLUSH_TOKEN
|
|
102
|
+
if token is None:
|
|
103
|
+
if _CAPTURED:
|
|
104
|
+
pieces.append(_CAPTURED)
|
|
105
|
+
_CAPTURED = b""
|
|
106
|
+
return pieces, flushed
|
|
107
|
+
idx = _CAPTURED.find(token)
|
|
108
|
+
if idx < 0:
|
|
109
|
+
keep = _token_suffix_len(_CAPTURED, token)
|
|
110
|
+
if len(_CAPTURED) > keep:
|
|
111
|
+
pieces.append(_CAPTURED[: len(_CAPTURED) - keep])
|
|
112
|
+
_CAPTURED = _CAPTURED[-keep:] if keep else b""
|
|
113
|
+
return pieces, flushed
|
|
114
|
+
if idx:
|
|
115
|
+
pieces.append(_CAPTURED[:idx])
|
|
116
|
+
_CAPTURED = _CAPTURED[idx + len(token) :]
|
|
117
|
+
_FLUSH_TOKEN = None
|
|
118
|
+
flushed = True
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def _publish_captured(pieces: list[bytes], flushed: bool) -> None:
|
|
122
|
+
global _CAPTURE_DECODER
|
|
123
|
+
for piece in pieces:
|
|
124
|
+
text = _CAPTURE_DECODER.decode(piece)
|
|
125
|
+
if text:
|
|
126
|
+
_forward_captured_stdout(text)
|
|
127
|
+
if not flushed:
|
|
128
|
+
return
|
|
129
|
+
tail = _CAPTURE_DECODER.decode(b"", final=True)
|
|
130
|
+
if tail:
|
|
131
|
+
_forward_captured_stdout(tail)
|
|
132
|
+
_CAPTURE_DECODER = codecs.getincrementaldecoder("utf-8")(errors="replace")
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def _forward_captured_stdout(text: str) -> None:
|
|
136
|
+
rid = _STATE.current_id
|
|
137
|
+
if not text:
|
|
138
|
+
return
|
|
139
|
+
if rid is None:
|
|
140
|
+
_RAW_STDERR.write(text)
|
|
141
|
+
_RAW_STDERR.flush()
|
|
142
|
+
return
|
|
143
|
+
_emit({"type": "stdout", "id": rid, "data": text})
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def _pump_captured_stdout(read_fd: int) -> None:
|
|
147
|
+
global _CAPTURED
|
|
148
|
+
while True:
|
|
149
|
+
try:
|
|
150
|
+
chunk = os.read(read_fd, 65536)
|
|
151
|
+
except OSError:
|
|
152
|
+
return
|
|
153
|
+
if not chunk:
|
|
154
|
+
return
|
|
155
|
+
with _CAP_LOCK:
|
|
156
|
+
_CAPTURED += chunk
|
|
157
|
+
pieces, flushed = _take_captured_locked()
|
|
158
|
+
_publish_captured(pieces, flushed)
|
|
159
|
+
if flushed:
|
|
160
|
+
_FLUSH_EVENT.set()
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def _capture_user_stdout() -> None:
|
|
164
|
+
"""Move the host pipe off fd 1 so raw user writes are cell output."""
|
|
165
|
+
global _PROTO_FD
|
|
166
|
+
if _PROTO_FD is not None:
|
|
167
|
+
return
|
|
168
|
+
try:
|
|
169
|
+
sys.__stdout__.flush()
|
|
170
|
+
except Exception:
|
|
171
|
+
pass
|
|
172
|
+
proto = os.dup(1)
|
|
173
|
+
os.set_inheritable(proto, False)
|
|
174
|
+
_PROTO_FD = proto
|
|
175
|
+
read_fd, write_fd = os.pipe()
|
|
176
|
+
os.set_inheritable(read_fd, False)
|
|
177
|
+
os.dup2(write_fd, 1, inheritable=True)
|
|
178
|
+
os.close(write_fd)
|
|
179
|
+
threading.Thread(
|
|
180
|
+
target=_pump_captured_stdout,
|
|
181
|
+
args=(read_fd,),
|
|
182
|
+
name="gjc-stdout-capture",
|
|
183
|
+
daemon=True,
|
|
184
|
+
).start()
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def _flush_captured_stdout() -> None:
|
|
188
|
+
if _PROTO_FD is None:
|
|
189
|
+
return
|
|
190
|
+
token = b"\0gjcfd1:" + os.urandom(8).hex().encode("ascii") + b"\0"
|
|
191
|
+
with _CAP_LOCK:
|
|
192
|
+
global _FLUSH_TOKEN
|
|
193
|
+
_FLUSH_EVENT.clear()
|
|
194
|
+
_FLUSH_TOKEN = token
|
|
195
|
+
_write_all(1, token)
|
|
196
|
+
if _FLUSH_EVENT.wait(5):
|
|
197
|
+
return
|
|
198
|
+
with _CAP_LOCK:
|
|
199
|
+
if _FLUSH_TOKEN == token:
|
|
200
|
+
_FLUSH_TOKEN = None
|
|
201
|
+
pieces, flushed = _take_captured_locked()
|
|
202
|
+
else:
|
|
203
|
+
pieces, flushed = [], False
|
|
204
|
+
_publish_captured(pieces, flushed)
|
|
71
205
|
|
|
72
206
|
|
|
73
207
|
# ---------------------------------------------------------------------------
|
|
@@ -902,6 +1036,10 @@ def _handle_request(req: dict) -> None:
|
|
|
902
1036
|
_flush_matplotlib_figures()
|
|
903
1037
|
except Exception:
|
|
904
1038
|
pass
|
|
1039
|
+
try:
|
|
1040
|
+
_flush_captured_stdout()
|
|
1041
|
+
except Exception:
|
|
1042
|
+
pass
|
|
905
1043
|
|
|
906
1044
|
_emit({
|
|
907
1045
|
"type": "done",
|
|
@@ -930,6 +1068,7 @@ def _emit_error(rid: str, exc: BaseException) -> None:
|
|
|
930
1068
|
|
|
931
1069
|
|
|
932
1070
|
def main() -> None:
|
|
1071
|
+
_capture_user_stdout()
|
|
933
1072
|
sys.stdout = _StreamProxy("stdout")
|
|
934
1073
|
sys.stderr = _StreamProxy("stderr")
|
|
935
1074
|
_install_idle_sigint()
|
|
@@ -12,6 +12,7 @@ import type {
|
|
|
12
12
|
ShellRunResult,
|
|
13
13
|
} from "@gajae-code/natives";
|
|
14
14
|
import { postmortem } from "@gajae-code/utils";
|
|
15
|
+
import { projectDeclaredSpawnHookNames, stripProjectSpawnHooks } from "@gajae-code/utils/shell-config";
|
|
15
16
|
import { Settings, type ShellMinimizerSettings } from "../config/settings";
|
|
16
17
|
import { formatCrashDiagnosticNotice, writeCrashReport } from "../debug/crash-diagnostics";
|
|
17
18
|
import {
|
|
@@ -93,6 +94,20 @@ export function setShellFactoryForTests(factory: ShellFactory | undefined): void
|
|
|
93
94
|
shellFactoryForTests = factory;
|
|
94
95
|
}
|
|
95
96
|
|
|
97
|
+
function projectHookUnsetEnv(
|
|
98
|
+
unsetEnv: string[] | undefined,
|
|
99
|
+
env: Record<string, string> | undefined,
|
|
100
|
+
sessionEnv: Record<string, string>,
|
|
101
|
+
): string[] | undefined {
|
|
102
|
+
// Keys present in `sessionEnv` (e.g. the trusted HOME replacement) already
|
|
103
|
+
// override the ambient import, so only shadow the ones it does not carry.
|
|
104
|
+
const hooks = projectDeclaredSpawnHookNames().filter(
|
|
105
|
+
name => !Object.hasOwn(env ?? {}, name) && !Object.hasOwn(sessionEnv, name),
|
|
106
|
+
);
|
|
107
|
+
if (hooks.length === 0) return unsetEnv;
|
|
108
|
+
return [...(unsetEnv ?? []), ...hooks.filter(name => !unsetEnv?.includes(name))];
|
|
109
|
+
}
|
|
110
|
+
|
|
96
111
|
async function createShell(
|
|
97
112
|
options?: ShellOptions,
|
|
98
113
|
onTerminal?: (shell: Shell) => void,
|
|
@@ -414,13 +429,67 @@ async function resolveShellCwd(cwd: string | undefined): Promise<string | undefi
|
|
|
414
429
|
}
|
|
415
430
|
}
|
|
416
431
|
|
|
432
|
+
function presentMinimizerString(value: unknown): string | undefined {
|
|
433
|
+
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
type ShellMinimizerSource = {
|
|
437
|
+
getGroup(prefix: "shellMinimizer"): ShellMinimizerSettings;
|
|
438
|
+
getGlobal?(path: "shellMinimizer.settingsPath" | "shellMinimizer.settingsHash"): unknown;
|
|
439
|
+
getOverride?(path: "shellMinimizer.settingsPath"): unknown;
|
|
440
|
+
};
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* A repo `.gjc/settings.json` must not point the minimizer at a TOML file.
|
|
444
|
+
* The native gate loads that file only when `settingsHash` matches, and the
|
|
445
|
+
* pin is the user/global value. A user path or a runtime override is still
|
|
446
|
+
* passed through. A project path is passed only together with that pin.
|
|
447
|
+
*/
|
|
448
|
+
export function resolveTrustedShellMinimizer(settings: ShellMinimizerSource): ShellMinimizerSettings {
|
|
449
|
+
const group = settings.getGroup("shellMinimizer");
|
|
450
|
+
const globalPath =
|
|
451
|
+
typeof settings.getGlobal === "function"
|
|
452
|
+
? presentMinimizerString(settings.getGlobal("shellMinimizer.settingsPath"))
|
|
453
|
+
: undefined;
|
|
454
|
+
const globalHash =
|
|
455
|
+
typeof settings.getGlobal === "function"
|
|
456
|
+
? presentMinimizerString(settings.getGlobal("shellMinimizer.settingsHash"))
|
|
457
|
+
: undefined;
|
|
458
|
+
const overridePath =
|
|
459
|
+
typeof settings.getOverride === "function"
|
|
460
|
+
? presentMinimizerString(settings.getOverride("shellMinimizer.settingsPath"))
|
|
461
|
+
: undefined;
|
|
462
|
+
const effectivePath = presentMinimizerString(group.settingsPath);
|
|
463
|
+
|
|
464
|
+
let settingsPath: string | undefined;
|
|
465
|
+
if (overridePath && effectivePath === overridePath) {
|
|
466
|
+
settingsPath = overridePath;
|
|
467
|
+
} else if (globalPath) {
|
|
468
|
+
settingsPath = globalPath;
|
|
469
|
+
} else if (effectivePath && globalHash) {
|
|
470
|
+
settingsPath = effectivePath;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
// A runtime override of a different path is operator input, not the file the
|
|
474
|
+
// user hash pins. Every other admitted path is checked with that pin.
|
|
475
|
+
const unpinnedOverride = overridePath !== undefined && settingsPath === overridePath && settingsPath !== globalPath;
|
|
476
|
+
return {
|
|
477
|
+
...group,
|
|
478
|
+
settingsPath,
|
|
479
|
+
settingsHash: globalHash && settingsPath && !unpinnedOverride ? globalHash : undefined,
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
|
|
417
483
|
/** Translate `ShellMinimizerSettings` into native `MinimizerOptions`, or `undefined` when disabled. */
|
|
418
484
|
export function buildMinimizerOptions(group: ShellMinimizerSettings): MinimizerOptions | undefined {
|
|
419
485
|
if (!group.enabled) return undefined;
|
|
420
486
|
const maxCaptureBytes = Math.min(4 * 1024 * 1024, Math.max(1024, Math.trunc(group.maxCaptureBytes)));
|
|
487
|
+
const settingsPath = group.settingsPath || undefined;
|
|
488
|
+
const settingsHash = settingsPath && group.settingsHash ? group.settingsHash : undefined;
|
|
421
489
|
return {
|
|
422
490
|
enabled: true,
|
|
423
|
-
settingsPath
|
|
491
|
+
settingsPath,
|
|
492
|
+
...(settingsHash ? { settingsHash } : {}),
|
|
424
493
|
only: group.only.length > 0 ? group.only : undefined,
|
|
425
494
|
except: group.except.length > 0 ? group.except : undefined,
|
|
426
495
|
maxCaptureBytes,
|
|
@@ -429,12 +498,16 @@ export function buildMinimizerOptions(group: ShellMinimizerSettings): MinimizerO
|
|
|
429
498
|
|
|
430
499
|
export async function executeBash(command: string, options?: BashExecutorOptions): Promise<BashResult> {
|
|
431
500
|
const settings = options?.settings ?? (await Settings.init());
|
|
432
|
-
const { shell, env:
|
|
501
|
+
const { shell, env: configEnv, prefix } = settings.getShellConfig();
|
|
502
|
+
// The shell config env is shared with other consumers (Python kernels keep
|
|
503
|
+
// their own documented allowlist). Only the bash tool's shell must not see
|
|
504
|
+
// project hook variables such as BASH_ENV or GIT_CONFIG_*.
|
|
505
|
+
const shellEnv = stripProjectSpawnHooks({ ...configEnv });
|
|
433
506
|
const configuredPrefix = options?.ignoreShellPrefix ? undefined : prefix;
|
|
434
507
|
const snapshotPath =
|
|
435
508
|
!options?.disableShellSnapshot && shell.includes("bash") ? await getOrCreateSnapshot(shell, shellEnv) : null;
|
|
436
509
|
|
|
437
|
-
const minimizer = buildMinimizerOptions(settings
|
|
510
|
+
const minimizer = buildMinimizerOptions(resolveTrustedShellMinimizer(settings));
|
|
438
511
|
|
|
439
512
|
const commandCwd = await resolveShellCwd(options?.cwd);
|
|
440
513
|
const commandEnv = options?.env ? { ...NON_INTERACTIVE_ENV, ...options.env } : NON_INTERACTIVE_ENV;
|
|
@@ -580,7 +653,11 @@ export async function executeBash(command: string, options?: BashExecutorOptions
|
|
|
580
653
|
command: finalCommand,
|
|
581
654
|
cwd: commandCwd,
|
|
582
655
|
env: commandEnv,
|
|
583
|
-
|
|
656
|
+
// The native shell imports the host process environment before
|
|
657
|
+
// `sessionEnv`, and where it runs in-process (Windows) that environment
|
|
658
|
+
// holds the autoloaded project `.env`. Shadow project hook variables per
|
|
659
|
+
// run unless this call sets them explicitly.
|
|
660
|
+
unsetEnv: projectHookUnsetEnv(options?.unsetEnv, options?.env, shellEnv),
|
|
584
661
|
timeoutMs: executionTimeoutMs,
|
|
585
662
|
signal: runAbortController.signal,
|
|
586
663
|
};
|
|
@@ -8,6 +8,7 @@ import * as path from "node:path";
|
|
|
8
8
|
import * as readline from "node:readline";
|
|
9
9
|
import type { Process as NativeProcess } from "@gajae-code/natives";
|
|
10
10
|
import { isCompiledBinary } from "@gajae-code/utils/env";
|
|
11
|
+
import { projectSafeSpawnEnv } from "@gajae-code/utils/shell-config";
|
|
11
12
|
import { BASH_SHELL_SUPERVISOR_ARG, type BashShellWorkerRequest } from "./bash-shell-worker-protocol";
|
|
12
13
|
|
|
13
14
|
type NativeProcessBindings = { Process: typeof NativeProcess };
|
|
@@ -401,7 +402,9 @@ export async function runBashShellGuardian(): Promise<void> {
|
|
|
401
402
|
const [executable, ...args] = supervisorArgv();
|
|
402
403
|
const supervisor = childProcess.spawn(executable!, args, {
|
|
403
404
|
detached: process.platform !== "win32",
|
|
404
|
-
|
|
405
|
+
// This process reloaded the project `.env` on startup; keep project hook
|
|
406
|
+
// variables out of the supervisor -> runtime -> native shell chain.
|
|
407
|
+
env: projectSafeSpawnEnv(),
|
|
405
408
|
stdio: ["pipe", "inherit", "inherit"],
|
|
406
409
|
windowsHide: true,
|
|
407
410
|
});
|
|
@@ -8,6 +8,7 @@ import * as path from "node:path";
|
|
|
8
8
|
import * as readline from "node:readline";
|
|
9
9
|
import type { Process as NativeProcess } from "@gajae-code/natives";
|
|
10
10
|
import { isCompiledBinary } from "@gajae-code/utils/env";
|
|
11
|
+
import { projectSafeSpawnEnv } from "@gajae-code/utils/shell-config";
|
|
11
12
|
import { parseLinuxProcParentPid, parseLinuxProcStartTime, parseLinuxProcState } from "../gjc-runtime/linux-proc";
|
|
12
13
|
import { processIncarnation } from "../sdk/broker/process-incarnation";
|
|
13
14
|
import { fatalCatchableSignals, signalExitCode } from "./bash-shell-signals";
|
|
@@ -39,9 +40,10 @@ function darwinUniqueProcessId(pid: number, incarnation: string): string | undef
|
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
function runtimeArgv(): string[] {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
if (isCompiledBinary()) return [process.execPath, BASH_SHELL_RUNTIME_ARG];
|
|
44
|
+
// The supervisor already sanitized this environment. Do not let the source
|
|
45
|
+
// runtime reload a cwd `.env` that may have changed since the snapshot.
|
|
46
|
+
return [process.execPath, "--no-env-file", path.join(import.meta.dir, "bash-shell-runtime-entry.ts")];
|
|
45
47
|
}
|
|
46
48
|
|
|
47
49
|
function enableLinuxChildSubreaper(): void {
|
|
@@ -264,7 +266,9 @@ export async function runBashShellSupervisor(): Promise<void> {
|
|
|
264
266
|
const worker = childProcess.spawn(command!, args, {
|
|
265
267
|
cwd: process.cwd(),
|
|
266
268
|
detached: false,
|
|
267
|
-
|
|
269
|
+
// This process reloaded the project `.env` on startup; the runtime's native
|
|
270
|
+
// shell imports its environment wholesale, so drop project hook variables.
|
|
271
|
+
env: { ...projectSafeSpawnEnv(), GJC_ISOLATED_SHELL_RUNTIME: "1" },
|
|
268
272
|
stdio: ["pipe", "pipe", "pipe"],
|
|
269
273
|
windowsHide: true,
|
|
270
274
|
});
|
|
@@ -5,6 +5,7 @@ import * as path from "node:path";
|
|
|
5
5
|
import * as readline from "node:readline";
|
|
6
6
|
import type { ShellRunOptions } from "@gajae-code/natives";
|
|
7
7
|
import { isCompiledBinary } from "@gajae-code/utils/env";
|
|
8
|
+
import { projectSafeSpawnEnv } from "@gajae-code/utils/shell-config";
|
|
8
9
|
import {
|
|
9
10
|
BASH_SHELL_WORKER_ARG,
|
|
10
11
|
type BashShellWorkerRequest,
|
|
@@ -84,7 +85,10 @@ export class IsolatedShell {
|
|
|
84
85
|
this.#child = childProcess.spawn(command!, args, {
|
|
85
86
|
cwd: process.cwd(),
|
|
86
87
|
detached: process.platform !== "win32",
|
|
87
|
-
env
|
|
88
|
+
// The worker chain (guardian -> supervisor -> runtime) inherits this env and
|
|
89
|
+
// the native shell imports it wholesale, so project hook variables must be
|
|
90
|
+
// gone here, not only from the per-session overlay.
|
|
91
|
+
env: projectSafeSpawnEnv(),
|
|
88
92
|
stdio: ["pipe", "pipe", "pipe"],
|
|
89
93
|
windowsHide: true,
|
|
90
94
|
});
|
package/src/export/html/index.ts
CHANGED
|
@@ -2,7 +2,7 @@ import * as syncFs from "node:fs";
|
|
|
2
2
|
import * as fs from "node:fs/promises";
|
|
3
3
|
import * as path from "node:path";
|
|
4
4
|
import type { AgentState } from "@gajae-code/agent-core";
|
|
5
|
-
import { APP_NAME, isEnoent } from "@gajae-code/utils";
|
|
5
|
+
import { APP_NAME, isEnoent, redactCrashSecrets } from "@gajae-code/utils";
|
|
6
6
|
import { getResolvedThemeColors, getThemeExportColors } from "../../modes/theme/theme";
|
|
7
7
|
import { SessionManager } from "../../session/session-manager";
|
|
8
8
|
// Pre-generated template (created by scripts/generate-template.ts at publish time)
|
|
@@ -226,18 +226,18 @@ async function writeSessionHtml(
|
|
|
226
226
|
};
|
|
227
227
|
try {
|
|
228
228
|
sink.write(themedTemplate.slice(0, markerOffset));
|
|
229
|
-
writeEncoded(`{"header":${
|
|
229
|
+
writeEncoded(`{"header":${sessionEmbedJson(sm.getHeader())},"entries":[`);
|
|
230
230
|
let first = true;
|
|
231
231
|
sm.visitEntriesForExport(entry => {
|
|
232
|
-
writeEncoded(`${first ? "" : ","}${
|
|
232
|
+
writeEncoded(`${first ? "" : ","}${sessionEmbedJson(entry)}`);
|
|
233
233
|
first = false;
|
|
234
234
|
});
|
|
235
|
-
writeEncoded(`],"leafId":${
|
|
235
|
+
writeEncoded(`],"leafId":${sessionEmbedJson(sm.getLeafId())}`);
|
|
236
236
|
if (state?.systemPrompt !== undefined)
|
|
237
|
-
writeEncoded(`,"systemPrompt":${
|
|
237
|
+
writeEncoded(`,"systemPrompt":${sessionEmbedJson(state.systemPrompt.join("\n\n"))}`);
|
|
238
238
|
if (state?.tools !== undefined)
|
|
239
239
|
writeEncoded(
|
|
240
|
-
`,"tools":${
|
|
240
|
+
`,"tools":${sessionEmbedJson(state.tools.map(tool => ({ name: tool.name, description: tool.description })))}`,
|
|
241
241
|
);
|
|
242
242
|
writeEncoded("}");
|
|
243
243
|
const final = encoder.finish();
|
|
@@ -253,6 +253,46 @@ async function writeSessionHtml(
|
|
|
253
253
|
}
|
|
254
254
|
}
|
|
255
255
|
|
|
256
|
+
/**
|
|
257
|
+
* Own `__proto__` stays a data property; a normal object would turn that assignment into the prototype.
|
|
258
|
+
* Each field is redacted together with its key. A detached leaf such as `abcdefgh1234` under `password`
|
|
259
|
+
* would otherwise miss the key-aware rule. If that serialized field does not parse, the value is
|
|
260
|
+
* redacted on its own so a quotation mark inside a labeled secret cannot break the export.
|
|
261
|
+
*/
|
|
262
|
+
function redactJsonValue(value: unknown): unknown {
|
|
263
|
+
if (typeof value === "string") return redactCrashSecrets(value);
|
|
264
|
+
if (Array.isArray(value)) return value.map(item => redactJsonValue(item));
|
|
265
|
+
if (value === null || typeof value !== "object") return value;
|
|
266
|
+
const source = value as Record<string, unknown>;
|
|
267
|
+
const redacted = Object.create(null) as Record<string, unknown>;
|
|
268
|
+
for (const key of Object.keys(source)) redacted[key] = redactKeyedValue(key, source[key]);
|
|
269
|
+
return redacted;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const CREDENTIAL_JSON_KEY =
|
|
273
|
+
/^(?:api[_-]?key|apikey|access[_-]?token|refresh[_-]?token|id[_-]?token|session[_-]?id|session[_-]?token|client[_-]?secret|secret[_-]?key|aws[_-]?secret[_-]?access[_-]?key|secret[_-]?access[_-]?key|password|passwd|authorization)$/i;
|
|
274
|
+
|
|
275
|
+
function redactKeyedValue(key: string, value: unknown): unknown {
|
|
276
|
+
// A recognized key is authoritative for every JSON type, including a short
|
|
277
|
+
// string, a number, an array, or a nested object.
|
|
278
|
+
if (CREDENTIAL_JSON_KEY.test(key)) return "«redacted»";
|
|
279
|
+
if (typeof value !== "string") return redactJsonValue(value);
|
|
280
|
+
const encoded = JSON.stringify({ [key]: value });
|
|
281
|
+
try {
|
|
282
|
+
const parsed = JSON.parse(redactCrashSecrets(encoded)) as Record<string, unknown>;
|
|
283
|
+
if (parsed !== null && typeof parsed === "object" && Object.hasOwn(parsed, key)) return parsed[key];
|
|
284
|
+
} catch {
|
|
285
|
+
// A quote inside a credential field can make the redacted JSON invalid.
|
|
286
|
+
// Mask that field instead of exporting the raw value without its key.
|
|
287
|
+
if (CREDENTIAL_JSON_KEY.test(key)) return "«redacted»";
|
|
288
|
+
}
|
|
289
|
+
return redactCrashSecrets(value);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function sessionEmbedJson(value: unknown): string {
|
|
293
|
+
return JSON.stringify(redactJsonValue(JSON.parse(JSON.stringify(value))));
|
|
294
|
+
}
|
|
295
|
+
|
|
256
296
|
/** Export session to HTML using SessionManager and AgentState. */
|
|
257
297
|
export async function exportSessionToHtml(
|
|
258
298
|
sm: SessionManager,
|
|
@@ -21,9 +21,11 @@ export class CustomToolAdapter<TParams extends TSchema = TSchema, TDetails = any
|
|
|
21
21
|
private tool: CustomTool<TParams, TDetails>,
|
|
22
22
|
private getContext: () => CustomToolContext,
|
|
23
23
|
) {
|
|
24
|
-
|
|
24
|
+
// Own these before proxying: with useDefineForClassFields off, the uninitialized fields do not
|
|
25
|
+
// exist yet, so applyToolProxy would install getter-only accessors that reject assignment.
|
|
25
26
|
this.strict = tool.strict;
|
|
26
27
|
this.concurrency = tool.concurrency;
|
|
28
|
+
applyToolProxy(tool, this);
|
|
27
29
|
}
|
|
28
30
|
|
|
29
31
|
execute(
|
|
@@ -791,7 +791,14 @@ export async function discoverAndLoadExtensions(
|
|
|
791
791
|
...(options.profileAuthority === undefined ? {} : { profileAuthority: options.profileAuthority }),
|
|
792
792
|
...(options.settings === undefined ? {} : { settings: options.settings }),
|
|
793
793
|
});
|
|
794
|
-
|
|
794
|
+
// Filter project modules before first-wins name deduplication. `items` has
|
|
795
|
+
// already deduplicated, so a project module could otherwise shadow the
|
|
796
|
+
// same-named user module before being rejected here.
|
|
797
|
+
const seenDiscoveredNames = new Set<string>();
|
|
798
|
+
for (const ext of discovered.all) {
|
|
799
|
+
if (ext._source.provider === "native" && ext.level === "project") continue;
|
|
800
|
+
if (seenDiscoveredNames.has(ext.name)) continue;
|
|
801
|
+
seenDiscoveredNames.add(ext.name);
|
|
795
802
|
if (ext._source.provider !== "native") continue;
|
|
796
803
|
addPath(ext.path);
|
|
797
804
|
}
|
|
@@ -10,7 +10,7 @@ import * as https from "node:https";
|
|
|
10
10
|
import * as net from "node:net";
|
|
11
11
|
import * as os from "node:os";
|
|
12
12
|
import * as path from "node:path";
|
|
13
|
-
import { isEnoent, logger } from "@gajae-code/utils";
|
|
13
|
+
import { isEnoent, logger, redactCrashSecrets } from "@gajae-code/utils";
|
|
14
14
|
import * as git from "../../../utils/git";
|
|
15
15
|
import { isPrivateOrSpecialAddress, validatePublicHttpUrl } from "../../../web/insane/url-guard";
|
|
16
16
|
|
|
@@ -461,7 +461,7 @@ async function cloneAndReadCatalog(url: string, cacheDir: string): Promise<Fetch
|
|
|
461
461
|
const tmpDir = path.join(cacheDir, `.tmp-clone-${Date.now()}`);
|
|
462
462
|
await fs.mkdir(cacheDir, { recursive: true });
|
|
463
463
|
|
|
464
|
-
logger.debug(`[marketplace] cloning ${url} → ${tmpDir}`);
|
|
464
|
+
logger.debug(redactCrashSecrets(`[marketplace] cloning ${url} → ${tmpDir}`));
|
|
465
465
|
await git.clone(url, tmpDir);
|
|
466
466
|
|
|
467
467
|
const catalogPath = path.join(tmpDir, CATALOG_RELATIVE_PATH);
|
|
@@ -18,7 +18,15 @@ import * as fsp from "node:fs/promises";
|
|
|
18
18
|
import * as os from "node:os";
|
|
19
19
|
import * as path from "node:path";
|
|
20
20
|
import { exactRemoveDirectoryTree, exactUnlink, snapshotDirectoryTree } from "@gajae-code/natives";
|
|
21
|
-
import {
|
|
21
|
+
import {
|
|
22
|
+
getAgentDir,
|
|
23
|
+
getBlobsDir,
|
|
24
|
+
getSessionsDir,
|
|
25
|
+
isEnoent,
|
|
26
|
+
loadedProjectEnvSnapshot,
|
|
27
|
+
VERSION,
|
|
28
|
+
} from "@gajae-code/utils";
|
|
29
|
+
import { type ProjectEnvSnapshot, projectEnvSnapshot } from "@gajae-code/utils/env-file";
|
|
22
30
|
import { getDefault } from "../config/settings-schema";
|
|
23
31
|
import { listHarnessRootRegistriesForGc } from "../harness-control-plane/storage";
|
|
24
32
|
import { SessionIndex } from "../sdk/broker/session-index";
|
|
@@ -54,6 +62,7 @@ import {
|
|
|
54
62
|
} from "./empty-delete-gc";
|
|
55
63
|
import { buildGcReportText } from "./gc-render";
|
|
56
64
|
import { collectSessionScopeUsage, type GcSessionScopeUsage, shouldReportSessionScope } from "./gc-session-scope";
|
|
65
|
+
import { trustedCoordinatorEnv } from "./sidecar-bootstrap-env";
|
|
57
66
|
|
|
58
67
|
export type GcStore = "harness_leases" | "file_locks" | "tmux_sessions" | "registry_entries" | "local_roots";
|
|
59
68
|
|
|
@@ -456,8 +465,21 @@ export function computeExitCode(report: GcReport): number {
|
|
|
456
465
|
return 0;
|
|
457
466
|
}
|
|
458
467
|
|
|
459
|
-
|
|
460
|
-
|
|
468
|
+
/**
|
|
469
|
+
* Operator override only. Same project-dotenv rule as `getAgentDir()`: a value
|
|
470
|
+
* the checkout declares is ignored, and an operator value it does not declare
|
|
471
|
+
* is kept. A repository `.env` must not choose the tree `gjc gc` reclaims.
|
|
472
|
+
*
|
|
473
|
+
* `snapshot` is the dotenv provenance from when that environment was loaded.
|
|
474
|
+
* `process.env` uses the module-load snapshot. A separate env map uses the
|
|
475
|
+
* snapshot taken with it before awaited GC work. Neither path rereads files later.
|
|
476
|
+
*/
|
|
477
|
+
function resolveGcAgentDir(env: NodeJS.ProcessEnv, snapshot: ProjectEnvSnapshot): string {
|
|
478
|
+
return (
|
|
479
|
+
trustedCoordinatorEnv("GJC_CODING_AGENT_DIR", env, snapshot) ||
|
|
480
|
+
trustedCoordinatorEnv("PI_CODING_AGENT_DIR", env, snapshot) ||
|
|
481
|
+
getAgentDir()
|
|
482
|
+
);
|
|
461
483
|
}
|
|
462
484
|
|
|
463
485
|
/**
|
|
@@ -572,6 +594,10 @@ export async function runGjcGcCommand(
|
|
|
572
594
|
adapters?: GcStoreAdapter[],
|
|
573
595
|
diskPolicy?: Partial<GcDiskPolicy>,
|
|
574
596
|
): Promise<GcRunResult> {
|
|
597
|
+
// process.env was loaded before this module. Reusing that snapshot keeps a
|
|
598
|
+
// project value rejected after its declaration is removed or changed.
|
|
599
|
+
// A caller-supplied env is not that load: bind its cwd files once, here.
|
|
600
|
+
const loadedProjectEnv = env === process.env ? loadedProjectEnvSnapshot() : projectEnvSnapshot(cwd);
|
|
575
601
|
let parsed: ParsedGcArgs;
|
|
576
602
|
try {
|
|
577
603
|
parsed = parseGcArgs(argv);
|
|
@@ -656,17 +682,18 @@ export async function runGjcGcCommand(
|
|
|
656
682
|
const ordinaryPrune = parsed.prune && (emptyDeleteReceipts?.errors.length ?? 0) === 0;
|
|
657
683
|
const report = await collectGcReport(resolvedAdapters, ctx, ordinaryPrune);
|
|
658
684
|
report.operation = parsed.repairSessionIndex ? "repair_session_index" : parsed.prune ? "prune" : "dry_run";
|
|
659
|
-
|
|
685
|
+
const agentDir = resolveGcAgentDir(env, loadedProjectEnv);
|
|
686
|
+
report.session_index = await collectSessionIndexHealth(parsed.repairSessionIndex, agentDir);
|
|
660
687
|
if (parsed.disk) {
|
|
661
688
|
report.disk = await collectGcDiskReport({
|
|
662
|
-
agentDir
|
|
689
|
+
agentDir,
|
|
663
690
|
env,
|
|
664
691
|
policy: resolveGcDiskPolicy(diskPolicy),
|
|
665
692
|
prune: ordinaryPrune,
|
|
666
693
|
});
|
|
667
694
|
}
|
|
668
695
|
if (emptyDeleteReceipts) report.empty_delete_receipts = emptyDeleteReceipts;
|
|
669
|
-
const scopeUsage = await collectGcSessionScope(cwd,
|
|
696
|
+
const scopeUsage = await collectGcSessionScope(cwd, agentDir);
|
|
670
697
|
if (scopeUsage && shouldReportSessionScope(scopeUsage)) report.session_scope = scopeUsage;
|
|
671
698
|
const sessionIndexFailed =
|
|
672
699
|
report.session_index?.status === "corrupt" ||
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
MANAGED_OWNER_PREDECESSOR_RUN_ID_ENV,
|
|
14
14
|
MANAGED_OWNER_PREDECESSOR_TOKEN_ENV,
|
|
15
15
|
MANAGED_OWNER_TRANSCRIPT_PATH_ENV,
|
|
16
|
+
trustedManagedOwnerLaunchSessionFile,
|
|
16
17
|
} from "./managed-owner-admission";
|
|
17
18
|
import {
|
|
18
19
|
MANAGED_OWNER_INCARNATION_ENV,
|
|
@@ -1249,8 +1250,12 @@ function prepareManagedOwnerLifecycle(plan: TmuxLaunchPlan, context: TmuxLaunchC
|
|
|
1249
1250
|
[MANAGED_OWNER_PREDECESSOR_GENERATION_ENV]: replacement.generation,
|
|
1250
1251
|
[MANAGED_OWNER_PREDECESSOR_RUN_ID_ENV]: replacement.runId,
|
|
1251
1252
|
[MANAGED_OWNER_PREDECESSOR_INCARNATION_ENV]: replacement.incarnation,
|
|
1252
|
-
[MANAGED_OWNER_TRANSCRIPT_PATH_ENV]:
|
|
1253
|
-
|
|
1253
|
+
[MANAGED_OWNER_TRANSCRIPT_PATH_ENV]: trustedManagedOwnerLaunchSessionFile(
|
|
1254
|
+
{
|
|
1255
|
+
GJC_SESSION_FILE: context.env?.GJC_SESSION_FILE ?? process.env.GJC_SESSION_FILE,
|
|
1256
|
+
},
|
|
1257
|
+
plan.cwd,
|
|
1258
|
+
),
|
|
1254
1259
|
}
|
|
1255
1260
|
: {}),
|
|
1256
1261
|
},
|