indusagi-coding-agent 0.2.13 → 0.2.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -0
- package/dist/entry.js +1762 -958
- package/dist/guardrails.js +145 -35
- package/dist/index.js +1810 -1006
- package/dist/review-agent.js +344 -60
- package/dist/review-server.js +346 -62
- package/package.json +12 -3
- package/src/boot/auth-vault.ts +4 -5
- package/src/boot/runners/addon-wiring.ts +111 -3
- package/src/boot/runners/benchmark-integrity.ts +107 -0
- package/src/boot/runners/repl-runner.ts +2 -1
- package/src/boot/runners/session.ts +18 -8
- package/src/boot/server-token.ts +20 -11
- package/src/briefing/context-docs.ts +144 -12
- package/src/channels/oneshot.ts +6 -0
- package/src/conductor/conductor.ts +67 -12
- package/src/conductor/contract.ts +4 -3
- package/src/conductor/transcript-store/store.ts +4 -8
- package/src/console/auth-status.ts +9 -4
- package/src/console/components/AgentsView.ts +2 -2
- package/src/console/components/BackgroundAgents.ts +15 -2
- package/src/console/components/Banner.ts +20 -9
- package/src/console/components/Composer.ts +57 -82
- package/src/console/components/ScrollViewport.ts +46 -0
- package/src/console/components/TerminalConsole.ts +165 -162
- package/src/console/components/WorkingIndicator.ts +6 -5
- package/src/console/components/banner-sweep.ts +5 -1
- package/src/console/components/welcome.ts +3 -3
- package/src/console/contract.ts +10 -5
- package/src/console/index.ts +7 -7
- package/src/console/input/complete.ts +2 -2
- package/src/console/input/dir-reader.ts +3 -1
- package/src/console/input/graphemes.ts +22 -0
- package/src/console/input/keymap.ts +6 -4
- package/src/console/mount.ts +45 -3
- package/src/console/overlays/auth.ts +28 -8
- package/src/console/overlays/boards.ts +13 -40
- package/src/console/overlays/host.ts +10 -0
- package/src/console/overlays/pickers.ts +35 -30
- package/src/console/reducer.ts +12 -5
- package/src/console/slash/builtins.ts +4 -3
- package/src/console/slash/commands/integrations.ts +13 -14
- package/src/console/slash/commands/transcript.ts +3 -8
- package/src/console/slash/commands/workbench.ts +2 -2
- package/src/console/test-support.ts +48 -0
- package/src/integrations/sarvam/config.ts +15 -13
- package/src/kit/external-editor.ts +17 -1
- package/src/kit/run-shell.ts +31 -0
- package/src/launch/invocation/flags.ts +12 -0
- package/src/launch/login.ts +23 -10
- package/src/launch/oauth.ts +3 -0
- package/src/models.ts +78 -2
- package/src/security/private-state.ts +122 -0
- package/src/window-budget/summarize/condense.ts +4 -1
- package/src/workspace/brand.ts +1 -1
- package/src/workspace/locator.ts +2 -2
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import {
|
|
2
|
+
chmodSync,
|
|
3
|
+
closeSync,
|
|
4
|
+
constants,
|
|
5
|
+
fchmodSync,
|
|
6
|
+
fstatSync,
|
|
7
|
+
lstatSync,
|
|
8
|
+
mkdirSync,
|
|
9
|
+
openSync,
|
|
10
|
+
readFileSync,
|
|
11
|
+
renameSync,
|
|
12
|
+
rmSync,
|
|
13
|
+
writeFileSync,
|
|
14
|
+
} from "node:fs";
|
|
15
|
+
import { randomUUID } from "node:crypto";
|
|
16
|
+
import { dirname } from "node:path";
|
|
17
|
+
|
|
18
|
+
const PRIVATE_DIRECTORY_MODE = 0o700;
|
|
19
|
+
const PRIVATE_FILE_MODE = 0o600;
|
|
20
|
+
const NO_FOLLOW = constants.O_NOFOLLOW ?? 0;
|
|
21
|
+
|
|
22
|
+
function isPosix(): boolean {
|
|
23
|
+
return process.platform !== "win32";
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function assertOwned(uid: number, path: string): void {
|
|
27
|
+
if (!isPosix() || process.getuid === undefined) return;
|
|
28
|
+
if (uid !== process.getuid()) {
|
|
29
|
+
throw new Error(`Refusing sensitive path not owned by the current user: ${path}`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function ensurePrivateDirectorySync(path: string): void {
|
|
34
|
+
try {
|
|
35
|
+
const current = lstatSync(path);
|
|
36
|
+
if (current.isSymbolicLink() || !current.isDirectory()) {
|
|
37
|
+
throw new Error(`Refusing non-directory sensitive state path: ${path}`);
|
|
38
|
+
}
|
|
39
|
+
assertOwned(current.uid, path);
|
|
40
|
+
} catch (error) {
|
|
41
|
+
if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
|
|
42
|
+
mkdirSync(path, { recursive: true, mode: PRIVATE_DIRECTORY_MODE });
|
|
43
|
+
}
|
|
44
|
+
if (isPosix()) chmodSync(path, PRIVATE_DIRECTORY_MODE);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function validateExistingFile(path: string): void {
|
|
48
|
+
try {
|
|
49
|
+
const current = lstatSync(path);
|
|
50
|
+
if (current.isSymbolicLink() || !current.isFile()) {
|
|
51
|
+
throw new Error(`Refusing non-regular sensitive file: ${path}`);
|
|
52
|
+
}
|
|
53
|
+
assertOwned(current.uid, path);
|
|
54
|
+
} catch (error) {
|
|
55
|
+
if ((error as NodeJS.ErrnoException).code === "ENOENT") return;
|
|
56
|
+
throw error;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function readPrivateFileSync(path: string): string | null {
|
|
61
|
+
ensurePrivateDirectorySync(dirname(path));
|
|
62
|
+
try {
|
|
63
|
+
validateExistingFile(path);
|
|
64
|
+
const fd = openSync(path, constants.O_RDONLY | NO_FOLLOW);
|
|
65
|
+
try {
|
|
66
|
+
const current = fstatSync(fd);
|
|
67
|
+
if (!current.isFile()) throw new Error(`Refusing non-regular sensitive file: ${path}`);
|
|
68
|
+
assertOwned(current.uid, path);
|
|
69
|
+
if (isPosix()) fchmodSync(fd, PRIVATE_FILE_MODE);
|
|
70
|
+
return readFileSync(fd, "utf8");
|
|
71
|
+
} finally {
|
|
72
|
+
closeSync(fd);
|
|
73
|
+
}
|
|
74
|
+
} catch (error) {
|
|
75
|
+
if ((error as NodeJS.ErrnoException).code === "ENOENT") return null;
|
|
76
|
+
throw error;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function atomicWritePrivateFileSync(path: string, content: string): void {
|
|
81
|
+
const parent = dirname(path);
|
|
82
|
+
ensurePrivateDirectorySync(parent);
|
|
83
|
+
validateExistingFile(path);
|
|
84
|
+
const temporary = `${path}.tmp-${process.pid}-${randomUUID()}`;
|
|
85
|
+
let fd: number | undefined;
|
|
86
|
+
try {
|
|
87
|
+
fd = openSync(
|
|
88
|
+
temporary,
|
|
89
|
+
constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | NO_FOLLOW,
|
|
90
|
+
PRIVATE_FILE_MODE,
|
|
91
|
+
);
|
|
92
|
+
writeFileSync(fd, content, "utf8");
|
|
93
|
+
if (isPosix()) fchmodSync(fd, PRIVATE_FILE_MODE);
|
|
94
|
+
closeSync(fd);
|
|
95
|
+
fd = undefined;
|
|
96
|
+
renameSync(temporary, path);
|
|
97
|
+
if (isPosix()) chmodSync(path, PRIVATE_FILE_MODE);
|
|
98
|
+
} catch (error) {
|
|
99
|
+
if (fd !== undefined) closeSync(fd);
|
|
100
|
+
try { rmSync(temporary, { force: true }); } catch { /* best effort cleanup */ }
|
|
101
|
+
throw error;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function appendPrivateFileSync(path: string, content: string): void {
|
|
106
|
+
ensurePrivateDirectorySync(dirname(path));
|
|
107
|
+
validateExistingFile(path);
|
|
108
|
+
const fd = openSync(
|
|
109
|
+
path,
|
|
110
|
+
constants.O_WRONLY | constants.O_APPEND | constants.O_CREAT | NO_FOLLOW,
|
|
111
|
+
PRIVATE_FILE_MODE,
|
|
112
|
+
);
|
|
113
|
+
try {
|
|
114
|
+
const current = fstatSync(fd);
|
|
115
|
+
if (!current.isFile()) throw new Error(`Refusing non-regular sensitive file: ${path}`);
|
|
116
|
+
assertOwned(current.uid, path);
|
|
117
|
+
if (isPosix()) fchmodSync(fd, PRIVATE_FILE_MODE);
|
|
118
|
+
writeFileSync(fd, content, "utf8");
|
|
119
|
+
} finally {
|
|
120
|
+
closeSync(fd);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
@@ -11,6 +11,8 @@ import { completeSimple } from "indusagi/ai";
|
|
|
11
11
|
import { CONDENSER_BRIEF, buildSummaryPrompt, flattenTranscript } from "./prompt.js";
|
|
12
12
|
|
|
13
13
|
export interface SummarizeDeps {
|
|
14
|
+
/** Literal user guidance; never interpreted as shell input. */
|
|
15
|
+
readonly instructions?: string;
|
|
14
16
|
/** Injectable completer (default: framework `completeSimple`). */
|
|
15
17
|
readonly complete?: CompleteFn;
|
|
16
18
|
/** The summarization model. When omitted, a deterministic local digest is used. */
|
|
@@ -71,7 +73,8 @@ export async function summarize(
|
|
|
71
73
|
const digest = localFallbackDigest(messages, deps.priorDigest);
|
|
72
74
|
return { message: toSummaryMessage(digest, scope), coveredCount };
|
|
73
75
|
}
|
|
74
|
-
const prompt = buildSummaryPrompt(messages, scope, deps.priorDigest)
|
|
76
|
+
const prompt = buildSummaryPrompt(messages, scope, deps.priorDigest) +
|
|
77
|
+
(deps.instructions ? `\n\nUser compaction guidance:\n${deps.instructions}` : "");
|
|
75
78
|
const context = {
|
|
76
79
|
systemPrompt: CONDENSER_BRIEF,
|
|
77
80
|
messages: [{ role: "user", content: prompt, timestamp: Date.now() }],
|
package/src/workspace/brand.ts
CHANGED
package/src/workspace/locator.ts
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
* Workspace Locator — turns the active {@link Brand} plus the environment into a
|
|
3
3
|
* single, fully-resolved {@link Workspace} record of absolute paths.
|
|
4
4
|
*/
|
|
5
|
-
import { mkdirSync } from "node:fs";
|
|
6
5
|
import { homedir } from "node:os";
|
|
7
6
|
import { isAbsolute, join, resolve } from "node:path";
|
|
8
7
|
import type { Brand, Workspace } from "../boot/contract.js";
|
|
9
8
|
import { BRAND } from "./brand.js";
|
|
9
|
+
import { ensurePrivateDirectorySync } from "../security/private-state.js";
|
|
10
10
|
|
|
11
11
|
export interface WorkspaceOverrides {
|
|
12
12
|
/** User home directory; defaults to {@link homedir}. */
|
|
@@ -97,7 +97,7 @@ export function createWorkspace(overrides: WorkspaceOverrides = {}): Workspace {
|
|
|
97
97
|
*/
|
|
98
98
|
export function ensureDirs(workspace: Workspace): Workspace {
|
|
99
99
|
for (const key of DIRECTORY_KEYS) {
|
|
100
|
-
|
|
100
|
+
ensurePrivateDirectorySync(workspace[key]);
|
|
101
101
|
}
|
|
102
102
|
return workspace;
|
|
103
103
|
}
|