@gajae-code/coding-agent 0.4.5 → 0.5.1
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 +62 -0
- package/dist/types/async/job-manager.d.ts +26 -0
- package/dist/types/cli/args.d.ts +1 -0
- package/dist/types/cli/list-models.d.ts +6 -0
- package/dist/types/commands/gc.d.ts +26 -0
- package/dist/types/commands/harness.d.ts +3 -0
- package/dist/types/config/file-lock-gc.d.ts +5 -0
- package/dist/types/config/file-lock.d.ts +7 -0
- package/dist/types/config/model-profile-activation.d.ts +11 -2
- package/dist/types/config/model-profiles.d.ts +7 -0
- package/dist/types/config/model-registry.d.ts +3 -0
- package/dist/types/config/model-resolver.d.ts +2 -0
- package/dist/types/config/models-config-schema.d.ts +30 -0
- package/dist/types/config/settings-schema.d.ts +4 -3
- package/dist/types/coordinator/contract.d.ts +1 -1
- package/dist/types/defaults/gjc/extensions/grok-build/index.d.ts +1 -0
- package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/index.d.ts +1 -0
- package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/models/catalog.d.ts +25 -0
- package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/payload/sanitize.d.ts +27 -0
- package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/provider/billing.d.ts +8 -0
- package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/provider/register.d.ts +5 -0
- package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/provider/stream.d.ts +10 -0
- package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/provider/usage.d.ts +2 -0
- package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/shared/base-url.d.ts +2 -0
- package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/shared/errors.d.ts +38 -0
- package/dist/types/defaults/gjc-grok-cli.d.ts +5 -0
- package/dist/types/extensibility/extensions/index.d.ts +1 -0
- package/dist/types/extensibility/extensions/prefix-command-bridge.d.ts +35 -0
- package/dist/types/gjc-runtime/deep-interview-recorder.d.ts +103 -0
- package/dist/types/gjc-runtime/deep-interview-runtime.d.ts +2 -0
- package/dist/types/gjc-runtime/deep-interview-state.d.ts +112 -0
- package/dist/types/gjc-runtime/gc-render.d.ts +6 -0
- package/dist/types/gjc-runtime/gc-runtime.d.ts +134 -0
- package/dist/types/gjc-runtime/ledger-event-renderer.d.ts +68 -0
- package/dist/types/gjc-runtime/team-gc.d.ts +7 -0
- package/dist/types/gjc-runtime/team-runtime.d.ts +5 -1
- package/dist/types/gjc-runtime/tmux-common.d.ts +14 -0
- package/dist/types/gjc-runtime/tmux-gc.d.ts +7 -0
- package/dist/types/gjc-runtime/tmux-sessions.d.ts +13 -0
- package/dist/types/harness-control-plane/gc-adapter.d.ts +3 -0
- package/dist/types/harness-control-plane/owner.d.ts +8 -1
- package/dist/types/harness-control-plane/receipt-spool.d.ts +19 -0
- package/dist/types/harness-control-plane/state-machine.d.ts +6 -1
- package/dist/types/harness-control-plane/storage.d.ts +20 -0
- package/dist/types/harness-control-plane/types.d.ts +4 -0
- package/dist/types/hindsight/mental-models.d.ts +5 -5
- package/dist/types/modes/components/hook-selector.d.ts +7 -1
- package/dist/types/modes/components/model-selector.d.ts +1 -12
- package/dist/types/modes/controllers/command-controller.d.ts +1 -0
- package/dist/types/modes/rpc/rpc-client.d.ts +2 -2
- package/dist/types/modes/rpc/rpc-mode.d.ts +16 -1
- package/dist/types/modes/rpc/rpc-types.d.ts +4 -1
- package/dist/types/modes/shared/agent-wire/deep-interview-gate.d.ts +13 -0
- package/dist/types/modes/shared/agent-wire/session-registry.d.ts +25 -0
- package/dist/types/modes/shared/agent-wire/unattended-action-policy.d.ts +2 -0
- package/dist/types/sdk.d.ts +5 -0
- package/dist/types/session/agent-session.d.ts +3 -1
- package/dist/types/session/blob-store.d.ts +59 -4
- package/dist/types/session/session-manager.d.ts +24 -6
- package/dist/types/session/streaming-output.d.ts +3 -2
- package/dist/types/session/tool-choice-queue.d.ts +6 -0
- package/dist/types/skill-state/workflow-hud.d.ts +14 -0
- package/dist/types/task/receipt.d.ts +1 -0
- package/dist/types/task/types.d.ts +7 -0
- package/dist/types/thinking-metadata.d.ts +16 -0
- package/dist/types/thinking.d.ts +3 -12
- package/dist/types/tools/ask.d.ts +15 -1
- package/dist/types/tools/index.d.ts +2 -0
- package/dist/types/tools/resolve.d.ts +0 -10
- package/dist/types/tools/subagent.d.ts +6 -0
- package/dist/types/utils/tool-choice.d.ts +14 -1
- package/package.json +7 -7
- package/src/async/job-manager.ts +52 -0
- package/src/cli/args.ts +3 -0
- package/src/cli/auth-broker-cli.ts +1 -0
- package/src/cli/list-models.ts +13 -1
- package/src/cli.ts +9 -4
- package/src/commands/gc.ts +22 -0
- package/src/commands/harness.ts +43 -5
- package/src/commands/launch.ts +2 -2
- package/src/commands/session.ts +3 -1
- package/src/config/file-lock-gc.ts +181 -0
- package/src/config/file-lock.ts +14 -0
- package/src/config/model-profile-activation.ts +15 -3
- package/src/config/model-profiles.ts +264 -56
- package/src/config/model-resolver.ts +9 -6
- package/src/config/models-config-schema.ts +1 -0
- package/src/config/settings-schema.ts +6 -3
- package/src/coordinator/contract.ts +1 -0
- package/src/coordinator-mcp/server.ts +513 -26
- package/src/cursor.ts +16 -2
- package/src/defaults/gjc/agent.models.grok-cli.yml +36 -0
- package/src/defaults/gjc/extensions/grok-build/index.ts +1 -0
- package/src/defaults/gjc/extensions/grok-build/package.json +7 -0
- package/src/defaults/gjc/extensions/grok-cli-vendor/biome.json +39 -0
- package/src/defaults/gjc/extensions/grok-cli-vendor/package.json +8 -0
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/index.ts +1 -0
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/models/catalog.ts +155 -0
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/payload/sanitize.ts +361 -0
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/provider/billing.ts +57 -0
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/provider/register.ts +99 -0
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/provider/stream.ts +50 -0
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/provider/usage.ts +56 -0
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/shared/base-url.ts +36 -0
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/shared/errors.ts +44 -0
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +131 -113
- package/src/defaults/gjc/skills/deep-interview/lateral-review-panel.md +49 -0
- package/src/defaults/gjc/skills/team/SKILL.md +3 -2
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +8 -2
- package/src/defaults/gjc-defaults.ts +7 -0
- package/src/defaults/gjc-grok-cli.ts +22 -0
- package/src/export/html/index.ts +13 -9
- package/src/extensibility/extensions/index.ts +1 -0
- package/src/extensibility/extensions/prefix-command-bridge.ts +128 -0
- package/src/gjc-runtime/deep-interview-recorder.ts +417 -0
- package/src/gjc-runtime/deep-interview-runtime.ts +18 -26
- package/src/gjc-runtime/deep-interview-state.ts +324 -0
- package/src/gjc-runtime/gc-render.ts +70 -0
- package/src/gjc-runtime/gc-runtime.ts +403 -0
- package/src/gjc-runtime/ledger-event-renderer.ts +164 -0
- package/src/gjc-runtime/ralplan-runtime.ts +58 -7
- package/src/gjc-runtime/state-renderer.ts +12 -3
- package/src/gjc-runtime/state-runtime.ts +46 -29
- package/src/gjc-runtime/team-gc.ts +49 -0
- package/src/gjc-runtime/team-runtime.ts +211 -8
- package/src/gjc-runtime/tmux-common.ts +29 -0
- package/src/gjc-runtime/tmux-gc.ts +176 -0
- package/src/gjc-runtime/tmux-sessions.ts +68 -12
- package/src/gjc-runtime/ultragoal-runtime.ts +517 -41
- package/src/gjc-runtime/workflow-manifest.generated.json +27 -1
- package/src/gjc-runtime/workflow-manifest.ts +16 -1
- package/src/harness-control-plane/gc-adapter.ts +184 -0
- package/src/harness-control-plane/owner.ts +89 -27
- package/src/harness-control-plane/receipt-spool.ts +128 -0
- package/src/harness-control-plane/state-machine.ts +27 -6
- package/src/harness-control-plane/storage.ts +93 -0
- package/src/harness-control-plane/types.ts +4 -0
- package/src/hindsight/mental-models.ts +17 -16
- package/src/internal-urls/docs-index.generated.ts +14 -8
- package/src/main.ts +7 -2
- package/src/modes/components/assistant-message.ts +26 -14
- package/src/modes/components/diff.ts +97 -0
- package/src/modes/components/hook-selector.ts +19 -0
- package/src/modes/components/model-selector.ts +370 -181
- package/src/modes/components/status-line/segments.ts +1 -1
- package/src/modes/components/tool-execution.ts +30 -13
- package/src/modes/controllers/command-controller.ts +25 -6
- package/src/modes/controllers/extension-ui-controller.ts +3 -0
- package/src/modes/controllers/selector-controller.ts +34 -42
- package/src/modes/rpc/rpc-client.ts +3 -2
- package/src/modes/rpc/rpc-mode.ts +187 -39
- package/src/modes/rpc/rpc-types.ts +5 -2
- package/src/modes/shared/agent-wire/command-dispatch.ts +279 -257
- package/src/modes/shared/agent-wire/command-validation.ts +11 -0
- package/src/modes/shared/agent-wire/deep-interview-gate.ts +30 -1
- package/src/modes/shared/agent-wire/session-registry.ts +109 -0
- package/src/modes/shared/agent-wire/unattended-action-policy.ts +24 -0
- package/src/modes/shared/agent-wire/unattended-run-controller.ts +23 -3
- package/src/modes/shared/agent-wire/unattended-session.ts +16 -1
- package/src/sdk.ts +46 -5
- package/src/secrets/obfuscator.ts +102 -27
- package/src/session/agent-session.ts +179 -25
- package/src/session/blob-store.ts +148 -6
- package/src/session/session-manager.ts +311 -60
- package/src/session/streaming-output.ts +185 -122
- package/src/session/tool-choice-queue.ts +23 -0
- package/src/setup/hermes/templates/operator-instructions.v1.md +7 -1
- package/src/skill-state/workflow-hud.ts +106 -10
- package/src/slash-commands/builtin-registry.ts +3 -2
- package/src/task/executor.ts +78 -6
- package/src/task/receipt.ts +5 -0
- package/src/task/render.ts +21 -1
- package/src/task/types.ts +8 -0
- package/src/thinking-metadata.ts +51 -0
- package/src/thinking.ts +26 -46
- package/src/tools/ask.ts +56 -1
- package/src/tools/bash.ts +1 -1
- package/src/tools/index.ts +2 -0
- package/src/tools/job.ts +3 -2
- package/src/tools/monitor.ts +36 -1
- package/src/tools/resolve.ts +93 -18
- package/src/tools/subagent-render.ts +9 -0
- package/src/tools/subagent.ts +26 -2
- package/src/utils/edit-mode.ts +1 -1
- package/src/utils/tool-choice.ts +45 -16
|
@@ -18,6 +18,8 @@ import * as fsSync from "node:fs";
|
|
|
18
18
|
import * as fs from "node:fs/promises";
|
|
19
19
|
import * as os from "node:os";
|
|
20
20
|
import * as path from "node:path";
|
|
21
|
+
import { appendReceiptToConfiguredSpool } from "./receipt-spool";
|
|
22
|
+
import type { ReceiptEnvelope } from "./receipts";
|
|
21
23
|
import type { EventEnvelope, ReceiptFamily, SessionState } from "./types";
|
|
22
24
|
|
|
23
25
|
interface HarnessRootRegistryEntry {
|
|
@@ -30,6 +32,18 @@ interface HarnessRootRegistry {
|
|
|
30
32
|
roots: HarnessRootRegistryEntry[];
|
|
31
33
|
}
|
|
32
34
|
|
|
35
|
+
export interface HarnessRootRegistryForGc {
|
|
36
|
+
sessionId: string;
|
|
37
|
+
roots: HarnessRootRegistryEntry[];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface HarnessRootRegistryListingForGc {
|
|
41
|
+
sessionId: string;
|
|
42
|
+
file: string;
|
|
43
|
+
roots: HarnessRootRegistryEntry[];
|
|
44
|
+
error?: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
33
47
|
interface ResolveHarnessSessionRootOptions {
|
|
34
48
|
expectedWorkspace?: string;
|
|
35
49
|
}
|
|
@@ -99,6 +113,64 @@ async function writeHarnessRootRegistry(
|
|
|
99
113
|
const file = harnessRootRegistryPath(registry.sessionId, env);
|
|
100
114
|
await writeJsonAtomicPrivate(file, registry);
|
|
101
115
|
}
|
|
116
|
+
|
|
117
|
+
function parseHarnessRootRegistryForGc(value: unknown, fallbackSessionId: string): HarnessRootRegistryForGc | null {
|
|
118
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
119
|
+
const registry = value as Record<string, unknown>;
|
|
120
|
+
if (typeof registry.sessionId !== "string" || !Array.isArray(registry.roots)) return null;
|
|
121
|
+
const roots: HarnessRootRegistryEntry[] = [];
|
|
122
|
+
for (const entry of registry.roots) {
|
|
123
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) return null;
|
|
124
|
+
const rootEntry = entry as Record<string, unknown>;
|
|
125
|
+
if (typeof rootEntry.root !== "string" || typeof rootEntry.updatedAt !== "string") return null;
|
|
126
|
+
roots.push({ root: rootEntry.root, updatedAt: rootEntry.updatedAt });
|
|
127
|
+
}
|
|
128
|
+
return { sessionId: registry.sessionId || fallbackSessionId, roots };
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/** @internal */
|
|
132
|
+
export async function listHarnessRootRegistriesForGc(
|
|
133
|
+
env: NodeJS.ProcessEnv = process.env,
|
|
134
|
+
): Promise<HarnessRootRegistryListingForGc[]> {
|
|
135
|
+
const dir = harnessRootRegistryDir(env);
|
|
136
|
+
let entries: string[];
|
|
137
|
+
try {
|
|
138
|
+
entries = await fs.readdir(dir);
|
|
139
|
+
} catch (error) {
|
|
140
|
+
const code = (error as NodeJS.ErrnoException).code;
|
|
141
|
+
if (code === "ENOENT") return [];
|
|
142
|
+
return [{ sessionId: "", file: dir, roots: [], error: (error as Error).message }];
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const registries: HarnessRootRegistryListingForGc[] = [];
|
|
146
|
+
for (const entry of entries) {
|
|
147
|
+
if (!entry.endsWith(".json")) continue;
|
|
148
|
+
const file = path.join(dir, entry);
|
|
149
|
+
const fallbackSessionId = entry.slice(0, -".json".length);
|
|
150
|
+
try {
|
|
151
|
+
const raw = await fs.readFile(file, "utf8");
|
|
152
|
+
const parsed = parseHarnessRootRegistryForGc(JSON.parse(raw), fallbackSessionId);
|
|
153
|
+
if (!parsed) {
|
|
154
|
+
registries.push({ sessionId: fallbackSessionId, file, roots: [], error: "malformed_registry" });
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
registries.push({ sessionId: parsed.sessionId, file, roots: parsed.roots });
|
|
158
|
+
} catch (error) {
|
|
159
|
+
registries.push({ sessionId: fallbackSessionId, file, roots: [], error: (error as Error).message });
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return registries;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/** @internal */
|
|
166
|
+
export async function rewriteHarnessRootRegistryForGc(file: string, registry: HarnessRootRegistryForGc): Promise<void> {
|
|
167
|
+
await writeJsonAtomicPrivate(file, registry);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/** @internal */
|
|
171
|
+
export async function removeHarnessRootRegistryFileForGc(file: string): Promise<void> {
|
|
172
|
+
await fs.rm(file, { force: true });
|
|
173
|
+
}
|
|
102
174
|
const SESSION_ID_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
|
|
103
175
|
export const MAX_UNIX_SOCKET_PATH_BYTES = 100;
|
|
104
176
|
|
|
@@ -227,6 +299,26 @@ async function readJson<T>(file: string): Promise<T | null> {
|
|
|
227
299
|
throw error;
|
|
228
300
|
}
|
|
229
301
|
}
|
|
302
|
+
function isReceiptEnvelope(value: unknown): value is ReceiptEnvelope<unknown> {
|
|
303
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
304
|
+
const envelope = value as Record<string, unknown>;
|
|
305
|
+
return (
|
|
306
|
+
typeof envelope.receiptId === "string" &&
|
|
307
|
+
typeof envelope.schemaVersion === "number" &&
|
|
308
|
+
typeof envelope.sessionId === "string" &&
|
|
309
|
+
typeof envelope.family === "string" &&
|
|
310
|
+
typeof envelope.valid === "boolean" &&
|
|
311
|
+
typeof envelope.createdAt === "string" &&
|
|
312
|
+
typeof envelope.source === "string" &&
|
|
313
|
+
envelope.subject !== null &&
|
|
314
|
+
typeof envelope.subject === "object" &&
|
|
315
|
+
envelope.evidence !== null &&
|
|
316
|
+
typeof envelope.evidence === "object" &&
|
|
317
|
+
envelope.artifactHashes !== null &&
|
|
318
|
+
typeof envelope.artifactHashes === "object" &&
|
|
319
|
+
typeof envelope.sha256 === "string"
|
|
320
|
+
);
|
|
321
|
+
}
|
|
230
322
|
|
|
231
323
|
export async function readSessionState(root: string, sessionId: string): Promise<SessionState | null> {
|
|
232
324
|
return readJson<SessionState>(sessionPaths(root, sessionId).state);
|
|
@@ -372,6 +464,7 @@ export async function writeReceiptImmutable(
|
|
|
372
464
|
path: file,
|
|
373
465
|
};
|
|
374
466
|
await fs.appendFile(paths.receiptsIndex, `${JSON.stringify(entry)}\n`, "utf8");
|
|
467
|
+
if (isReceiptEnvelope(value)) await appendReceiptToConfiguredSpool(value);
|
|
375
468
|
return entry;
|
|
376
469
|
}
|
|
377
470
|
|
|
@@ -210,6 +210,10 @@ export interface Observation {
|
|
|
210
210
|
rpcLive?: boolean;
|
|
211
211
|
/** ISO timestamp of the most recent RPC frame the owner observed, if any. */
|
|
212
212
|
rpcLastFrameAt?: string | null;
|
|
213
|
+
/** True only when owner/rpc/lifecycle gates indicate a prompt can be submitted now. */
|
|
214
|
+
readyForSubmit?: boolean;
|
|
215
|
+
/** Present when readyForSubmit is false; mirrors submit's nextAllowedActions reason. */
|
|
216
|
+
submitUnavailableReason?: string | null;
|
|
213
217
|
}
|
|
214
218
|
|
|
215
219
|
/** Input to the deterministic recovery classifier. */
|
|
@@ -295,12 +295,12 @@ export function summarizeMentalModel(model: MentalModelSummary): string {
|
|
|
295
295
|
* snapshot only; the diff is computed locally for display purposes.
|
|
296
296
|
*
|
|
297
297
|
* This is intentionally minimal — for "what changed" at a glance, not for a
|
|
298
|
-
* full structural diff. Each side is capped at `MAX_LCS_LINES` lines
|
|
299
|
-
* the
|
|
300
|
-
*
|
|
301
|
-
*
|
|
298
|
+
* full structural diff. Each side is capped at `MAX_LCS_LINES` lines before
|
|
299
|
+
* the Hunt-Szymanski LCS pass so a long curated model can never hang the TUI;
|
|
300
|
+
* output is then capped at `maxLines` so the rendered diff stays readable. The
|
|
301
|
+
* cap is signalled inline.
|
|
302
302
|
*/
|
|
303
|
-
/** Hard cap on input line count per side before LCS. Keeps
|
|
303
|
+
/** Hard cap on input line count per side before LCS. Keeps worst-case repeated-line matching bounded. */
|
|
304
304
|
export const MAX_LCS_LINES = 1_000;
|
|
305
305
|
|
|
306
306
|
export function diffMentalModelContent(previous: string | null, current: string, maxLines = 200): string {
|
|
@@ -346,24 +346,25 @@ export function diffMentalModelContent(previous: string | null, current: string,
|
|
|
346
346
|
}
|
|
347
347
|
|
|
348
348
|
function longestCommonSubsequence(a: string[], b: string[]): string[] {
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
349
|
+
return longestCommonSubsequenceDense(a, b);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
function longestCommonSubsequenceDense(a: string[], b: string[]): string[] {
|
|
353
|
+
const table: number[][] = Array.from({ length: a.length + 1 }, () => new Array(b.length + 1).fill(0));
|
|
354
|
+
for (let i = 0; i < a.length; i++) {
|
|
355
|
+
for (let j = 0; j < b.length; j++) {
|
|
356
|
+
table[i + 1]![j + 1] = a[i] === b[j] ? table[i]![j]! + 1 : Math.max(table[i + 1]![j]!, table[i]![j + 1]!);
|
|
356
357
|
}
|
|
357
358
|
}
|
|
358
359
|
const out: string[] = [];
|
|
359
|
-
let i =
|
|
360
|
-
let j =
|
|
360
|
+
let i = a.length;
|
|
361
|
+
let j = b.length;
|
|
361
362
|
while (i > 0 && j > 0) {
|
|
362
363
|
if (a[i - 1] === b[j - 1]) {
|
|
363
|
-
out.push(a[i - 1]);
|
|
364
|
+
out.push(a[i - 1]!);
|
|
364
365
|
i--;
|
|
365
366
|
j--;
|
|
366
|
-
} else if (table[i - 1][j] >= table[i][j - 1]) {
|
|
367
|
+
} else if (table[i - 1]![j]! >= table[i]![j - 1]!) {
|
|
367
368
|
i--;
|
|
368
369
|
} else {
|
|
369
370
|
j--;
|